![[object Object]](https://i0.wp.com/getmimo.wpcomstaging.com/wp-content/uploads/2025/12/43-Real-World-Front-End-Developer-Interview-Questions-2026.jpg?fit=1920%2C1080&ssl=1)
43 Real-World Front-End Developer Interview Questions (2026)
Front-end interviews can vary wildly. Some test layouts, others dive deep into JavaScript. This guide highlights the key questions and themes beginners should expect.
Some front-end interviews focus heavily on JavaScript, others dig into CSS layouts or React performance—it depends on the company and role.
We’ve helped thousands of Mimo users land their first dev jobs, and we know which questions keep coming up.
This guide covers beginner and intermediate front-end developer interview questions across key themes:
- Introduction to front-end development
- HTML & semantic markup
- CSS & layout techniques
- JavaScript fundamentals
- DOM & browser APIs
- Accessibility
- Performance & optimization
- Security basics
- Tooling & workflow
- Bonus: Modern React
| Want to build a portfolio and land a front‑end developer job quickly? Sign up for Mimo’s Front‑End Developer career path—start for free! |
Understanding the front-end developer role
Before diving into technical questions, make sure that you truly understand what the job involves day-to-day.
1. What does a front-end developer actually do?
This question seems basic, but interviewers ask it to see if you understand the full scope of the front-end developer role—not just “writing code.”
Front-end work sits inside the bigger world of web development, so the role often overlaps with how web applications behave behind the scenes. Even though you’re not building the database layer, you still collaborate with back-end developers or a full stack developer who handles both sides. Modern teams also expect familiarity with front-end frameworks, since they’re the backbone of most production projects.
How to answer
A front-end developer builds and maintains everything users see and interact with on a website or app.
But that’s only part of it.
The core responsibilities include:
- Designing page layouts and turning mockups into working code
- Writing HTML, CSS, and JavaScript (plus frameworks like React)
- Thinking about user experience—how easy is the site to use?
- Collaborating with designers and back-end developers
- Testing your work across browsers and devices
- Optimizing performance so pages load fast
The key point: front-end development’s key function is making things work right for users.
2. Which skills and technologies do you need to build user interfaces?
This question tests whether you know the full toolbox—including the coding languages and broader skills that make you effective on a team.
How to answer
The technical foundation is HTML, CSS, and JavaScript. But modern front-end work requires more:
- Frameworks and libraries like React, Vue, or Angular for building complex interfaces
- Version control (Git) to collaborate with other developers and track changes
- Responsive design so your sites work on phones, tablets, and desktops
- Basic UI/UX principles to understand why designers make certain choices
- Browser developer tools for debugging and testing
Beyond technical skills, you also need soft skills: clear communication, the ability to give and receive feedback, and enough design sense to spot when something looks off.
3. How do you work with designers and back-end developers?
This question checks if you can collaborate because front-end developers sit right in the middle between design and engineering.
How to answer
Front-end developers translate designs into code and connect that code to back-end systems. This means you’re constantly working with both sides.
With designers, you:
- Review mockups and ask questions early (before you start coding)
- Flag when something will be difficult or impossible to build
- Stay true to brand guidelines and design systems
With back-end developers, you:
- Understand how APIs work and what data you’ll receive
- Coordinate on data formats and error handling
- Test integrations together before launch
The key skill here is communication. You need to speak “designer” (talking about spacing, typography, user flows) and “engineer” (talking about endpoints, data structures, performance) fluently.
4. Why is user testing important in front-end development?
This question shows whether you think about the people using what you build, or just check if the code works and move on.
How to answer
User testing catches problems you’d never find on your own. You’re too close to the code to see it the way a real user does.
Testing matters because:
- Users do unexpected things. They click where you didn’t expect, use screen readers, or have slow internet connections.
- Bugs hide in real-world conditions. Something that works perfectly on your laptop might break on an older phone.
- Feedback drives improvement. After launch, user feedback tells you what to fix or build next.
Good front-end developers don’t simply ship code. They watch how people use it and iterate based on what they learn.
HTML and markup front-end interview questions
These questions test your understanding of how web pages are structured and whether you can write code that’s accessible, SEO-friendly, and works across devices.
5. Why is the <meta name=”viewport”> tag essential for responsive web design?
This question checks if you know how mobile browsers display websites—and why pages sometimes look tiny or zoomed out on phones without proper configuration.
How to answer
The viewport meta tag tells browsers how to scale your page on different screen sizes.
Without it, mobile browsers assume your site is designed for desktop (around 980px wide) and shrink everything to fit, making text unreadable.
For mobile-first design, use this tag in your <head>:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
Here’s what each part does:
- width=device-width sets the page width to match the device’s screen width
- initial-scale=1 prevents the browser from zooming in or out when the page loads
Without this tag, your media queries won’t work correctly on mobile devices—the browser won’t know to use the screen’s real width.
6. How do semantic elements like <header>, <nav>, <section>, and <article> improve accessibility?
This question tests whether you understand that HTML gives meaning to your content—meaning that screen readers and search engines use to interpret your page.
How to answer
Semantic elements describe what content is, not just how it looks.
When a screen reader encounters a <nav>, it announces “navigation” and lets users skip to it or past it. A <div> has no meaning—screen readers just see a generic container.
Here’s why this matters:
- Screen readers use semantic elements to help users navigate. Someone using a screen reader can jump directly to the <main> content or skip to the <nav>—they can’t do this with generic <div>s.
- Search engines understand your page structure better when you use semantic HTML, which can improve SEO (and AI search optimization).
- Other developers can read your code more easily when elements describe their purpose.
Common semantic elements include:
- <header> – introductory content or navigation
- <nav> – navigation links
- <main> – the primary content of the page
- <section> – a thematic grouping of content
- <article> – self-contained content (like a blog post)
- <footer> – footer content
A page built entirely with <div>s might look identical, but assistive technologies and search engines will struggle to understand it.
7. What role does alt text play for <img> elements?
This question checks if you know how to make images accessible to people who can’t see them. And whether you understand that alt text also helps search engines.
How to answer
The alt attribute provides text that describes an image.
Screen readers read this text aloud to users who can’t see the image, and browsers display it if the image fails to load.
Here’s an example:
<img src="golden-retriever.jpg" alt="A golden retriever playing fetch in a park">
Good alt text should:
- Describe what’s in the image concisely (usually one sentence).
- Skip phrases like “image of” or “picture of”—screen readers already announce it’s an image.
- Be empty (alt=””) for purely decorative images that don’t add information.
For SEO, search engines can’t “see” images—they rely on alt text to understand what an image shows.
Descriptive alt text helps your images appear in search results and gives search engines more context about your page.
8. How can you use the lang attribute to help screen readers and search engines?
This question comes up when the role involves multilingual websites. It tests whether you know how to tell browsers and assistive technologies what language your content is in.
How to answer
The lang attribute declares the language of your content. You set it on the <html> element for the whole page:
<html lang=”en”>
For pages with mixed languages, you can override it on specific elements:
<p>The French word for hello is <span lang=”fr”>bonjour</span>.</p>
Here’s why this matters:
- Screen readers use the lang attribute to pronounce words correctly. Without it, a screen reader might try to read French text with English pronunciation rules.
- Search engines use it to serve the right content to users searching in different languages.
- Browsers can offer translation or adjust hyphenation and quotation marks based on the language.
If you don’t set lang, the default is an empty string—which means screen readers have to guess, and they often guess wrong.
9. When would you use the <picture> element with <source> children?
This question tests whether you know how to serve different images based on screen size, pixel density, or file format support.
How to answer
The <picture> element lets you define multiple image sources and a fallback.
The browser picks the best option based on conditions you set—like viewport width or format support.
Here’s an example:
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="A mountain landscape at sunset">
</picture>
The browser checks each <source> in order:
- If it supports AVIF, it uses photo.avif
- If not but it supports WebP, it uses photo.webp
- If neither, it falls back to the <img> with photo.jpg
You can also serve different sizes based on viewport width:
<picture>
<source media="(min-width: 800px)" srcset="large.jpg">
<source media="(min-width: 400px)" srcset="medium.jpg">
<img src="small.jpg" alt="Product photo">
</picture>
This saves bandwidth on mobile devices and speeds up page loads by serving well-sized images.
10. How do the async and defer attributes on <script> affect loading and execution?
This question checks whether you understand how scripts can block page rendering, and how to prevent that.
How to answer
When a browser loads a web page, it reads your HTML from top to bottom and builds the page as it goes.
By default, when it hits a <script> tag, it stops everything, downloads the script, runs it, then continues building the page.
This delay is why pages sometimes feel slow to load.
Both async and defer tell the browser to download scripts in the background while it continues building the page.
The difference is when the script runs:
async:
- Downloads in the background
- Runs immediately when the download finishes, even if the page isn’t fully loaded yet
- If you have multiple async scripts, they might run in any order (whichever finishes downloading first runs first)
- Good for scripts that work independently, like analytics trackers
defer:
- Downloads in the background
- Waits until the entire page is loaded before running
- If you have multiple defer scripts, they run in the order they appear in your HTML
- Good for scripts that need to interact with your page content
For example:
<!-- Runs as soon as it downloads, might run before DOM is ready -->
<script async src="analytics.js"></script>
<!-- Waits for HTML parsing to finish, runs in order -->
<script defer src="app.js"></script>
The bottom line: Use defer for most scripts.
If your JavaScript tries to find an element on the page (like a button to add a click handler to), but that element hasn’t loaded yet, your code will fail.
defer prevents this by waiting until the page is fully built.
CSS and layout front-end interview questions
These questions test your understanding of styling, positioning, and modern layout techniques.
11. Describe the CSS box model and explain why you might set box-sizing: border-box.
This question is fundamental. If you don’t understand the box model, you’ll struggle to build layouts that behave the way you expect.
How to answer
Every element in CSS is a rectangular box. That box has four layers, from inside to outside:
- Content: The text, image, or child elements inside
- Padding: Empty space between the content and the border (like the margins inside a picture frame)
- Border: A visible line around the padding
- Margin: Empty space between this element and other elements nearby
Here’s the tricky part: by default, when you set width: 200px, you’re only setting the content size.
Padding and border get added on top of that.
So if you write:
.box {
width: 200px;
padding: 20px;
border: 5px solid black;
}
The actual width becomes 250px:
- 200px (content) + 20px (left padding) + 20px (right padding) + 5px (left border) + 5px (right border)
This causes problems.
Say you want two boxes side by side, each taking 50% of the screen.
You write width: 50%, but then you add padding for some breathing room—and suddenly they don’t fit because each box is now wider than 50%.
box-sizing: border-box fixes this.
With this setting, when you write width: 200px, the browser fits everything—content, padding, and border—inside those 200px.
If you add 20px of padding and a 5px border, the browser automatically makes the content area smaller (150px), so the total still equals 200px.
Most developers apply this to everything:
*, *::before, *::after {
box-sizing: border-box;
}
This makes sizing predictable. When you say 200px, you get 200px.
12. When should you choose CSS Grid over Flexbox?
This question tests whether you can pick the right tool for different layout problems.
How to answer
Both Grid and Flexbox help you arrange elements on a page, but they work differently:
- Flexbox works in one direction at a time—either a row (horizontal) or a column (vertical). Items flow along that line and can wrap to the next line if they run out of space.
- Grid works in two directions at once—you define rows and columns, and place items into that structure.
Here’s how to use them:
Use Flexbox when you’re arranging items in a line:
- A navigation bar with links in a row
- Centering something both vertically and horizontally
- Spacing out items evenly (like three cards in a row)
- Letting items wrap to new rows naturally when the screen gets smaller
Use Grid when you need to control both rows and columns:
- A full-page layout with a header at the top, sidebar on the left, and content on the right
- An image gallery where you want specific rows and columns
- Any design where things need to line up both horizontally and vertically
You can also combine them.
A common approach is this: use Grid for the overall page structure (header, sidebar, main content), then use Flexbox inside each section to arrange the items within it.
13. What is CSS specificity, and how does the cascade decide which rule applies?
This question tests whether you can predict which styles will win when multiple rules target the same element.
How to answer
When two CSS rules try to style the same element, the browser needs to decide which one wins. It uses specificity—a scoring system based on the types of selectors you use.
Each selector type has a different weight:
- Element selectors (like p, div, h1) are worth the least
- Class selectors (like .button, .header) are worth more
- ID selectors (like #main, #nav) are worth the most
The browser counts how many of each type appear in your selector.
A selector with one ID beats any number of classes. A selector with one class beats any number of elements.
For example:
p { color: blue; } /* Just an element - lowest specificity */
.intro { color: green; } /* One class - beats the element */
#welcome { color: red; } /* One ID - beats the class */
If an element matches all three rules, it turns red because the ID selector wins.
When two selectors have equal specificity, the one that appears later in your CSS wins. This is the “cascade” part—styles flow down and later rules override earlier ones.
However, there’s one exception: !important overrides everything, but using it often leads to messy code that’s hard to debug. Avoid it when you can.
14. Compare display: inline, block, inline-block, and contents.
This question checks whether you understand how elements take up space on a page.
How to answer
The display property controls how an element behaves in the layout:
display: block
- Takes up the full width available (stretches from left to right)
- Starts on a new line
- You can set width, height, margin, and padding in all directions
- Examples: <div>, <p>, <h1>
display: inline
- Only takes up as much width as its content needs
- Sits on the same line as neighboring inline elements (like words in a sentence)
- You cannot set width or height—the content determines the size
- Vertical margin and padding don’t push other elements away
- Examples: <span>, <a>, <strong>
display: inline-block
- Sits on the same line as other elements (like inline)
- But you can set width, height, and all margins/padding (like block)
- Useful when you want elements side by side but need control over their size
display: contents
- The element itself disappears from the layout
- Its children move up and behave as if the parent didn’t exist
- Useful in Grid or Flexbox when a wrapper element is breaking your layout
Here’s an example of contents:
<div class="grid">
<div class="wrapper" style="display: contents;">
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
Here, the .wrapper doesn’t create its own box—its children become direct grid items.
15. How do CSS custom properties (variables) and the clamp() function help with theming and responsive typography?
This question tests whether you know modern CSS features that reduce repetitive code and make designs more flexible.
How to answer
CSS custom properties (also called CSS variables) let you define a value once and reuse it throughout your stylesheet.
You create them with — and use them with var():
:root {
--main-color: #3498db;
--spacing: 16px;
}
.button {
background-color: var(--main-color);
padding: var(--spacing);
}
If you need to change the color later, you update it in one place instead of searching through your entire stylesheet.
For theming (like dark mode), you can redefine variables based on a class:
:root {
--background: white;
--text: black;
}
.dark-mode {
--background: #1a1a1a;
--text: white;
}
body {
background: var(--background);
color: var(--text);
}
When you add the dark-mode class to <body>, all elements using those variables update automatically.
clamp() lets you set a value that scales between a minimum and maximum. It takes three values: clamp(minimum, preferred, maximum).
For responsive font sizes:
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
This means:
- Never smaller than 1.5rem (24px)
- Ideally 4vw (4% of the viewport width), so it scales with screen size
- Never larger than 3rem (48px)
The text grows and shrinks smoothly as the browser window changes size—no media queries needed.
16. How do you structure mobile-first media queries and choose breakpoints?
This question tests whether you understand responsive design best practices.
How to answer
Mobile-first means you write your base CSS for small screens, then add styles for larger screens using min-width media queries.
For example:
/* Base styles for mobile */
.container {
padding: 16px;
}
/* When the screen is at least 600px wide */
@media (min-width: 600px) {
.container {
padding: 24px;
}
}
/* When the screen is at least 900px wide */
@media (min-width: 900px) {
.container {
padding: 32px;
max-width: 1200px;
}
}
Why mobile-first?
Because mobile styles are usually simpler—single columns, smaller fonts, stacked layouts. It’s easier to add complexity for larger screens than to strip it away for smaller ones.
For breakpoints (the widths where your layout changes), common values are:
- 600px – larger phones and small tablets
- 900px – tablets and small laptops
- 1200px – desktops
But don’t pick breakpoints based on device sizes—pick them based on where your content needs to change. For instance, if your design looks cramped at 750px, add a breakpoint there.
17. Compare position: relative, absolute, fixed, and sticky with examples.
This interview question tests whether you understand how to move elements around on a page and how each positioning method affects the layout.
How to answer
CSS gives you four main ways to position elements, and each one behaves differently.
- position: relative shifts an element from where it would normally appear, but the original space stays reserved—other elements don’t move to fill the gap.
- position: absolute pulls the element out of the normal flow entirely. Other elements act like it doesn’t exist. It positions itself relative to the nearest ancestor that has position set to anything other than static (the default). If no ancestor has this, it uses the page itself as the reference point.
- position: fixed also removes the element from the flow, but positions relative to the browser window. The element stays put even when the user scrolls.
- position: sticky is a hybrid. The element scrolls normally until it hits a threshold you set, then sticks in place like it’s fixed.
Here’s how each looks in code:
/* Shifts 10px down and 20px right from normal position */
.relative-box {
position: relative;
top: 10px;
left: 20px;
}
/* Sits in top-right corner of its positioned parent */
.parent { position: relative; }
.absolute-child {
position: absolute;
top: 0;
right: 0;
}
/* Stays at top of screen even when scrolling */
.fixed-header {
position: fixed;
top: 0;
width: 100%;
}
/* Scrolls normally, then sticks 20px from top */
.sticky-heading {
position: sticky;
top: 20px;
}
JavaScript and ES6 front-end interview questions
These questions cover core language features and modern syntax that you’ll use daily as a front-end developer.
18. What are the differences between var, let, and const?
This question is fundamental to modern JavaScript. Interviewers want to see that you understand how variables behave and why let and const have mostly replaced var.
How to answer
All three keywords create variables, but they differ in three ways: scope, reassignment, and hoisting.
Scope is where a variable exists and can be accessed.
var is function-scoped—the variable is available anywhere in the function, even if you declared it inside a smaller block like an if statement. let and const are block-scoped—they only exist inside the nearest set of curly braces.
if (true) {
var leaked = "hello";
let contained = "world";
}
console.log(leaked); // "hello" — var leaked out
console.log(contained); // Error — let stayed inside the block
Reassignment is whether you can change what a variable holds. var and let allow reassignment. const doesn’t—once you set it, you can’t point it at something else.
let count = 1;
count = 2; // works
const total = 1;
total = 2; // Error
However, there’s one thing that trips people up: const doesn’t really make objects unchangeable. You can still modify an object’s properties—you just can’t reassign the variable to a different object entirely.
const user = { name: "Alice" };
user.name = "Bob"; // works — modifying a property
user = { name: "Charlie" }; // Error — reassigning the variable
Hoisting is when JavaScript moves declarations to the top of their scope before running your code.
With var, the variable exists from the start of the function but holds undefined until the actual declaration. With let and const, the variable exists but can’t be accessed until the declaration—trying to use it earlier throws an error.
This inaccessible period is called the “temporal dead zone.”
Bottom line: Most developers now use const by default and switch to let only when they need to reassign. var is considered outdated because its function-scoping leads to accidental bugs.
19. How do template literals and tagged templates improve string handling?
This question tests whether you’re comfortable with modern JavaScript syntax and can work with strings.
How to answer
Before template literals, including variables in strings meant breaking out of quotes to concatenate:
const name = "Alice";
const greeting = "Hello, " + name + "!";
Template literals use backticks instead of quotes and let you embed expressions directly with ${}:
const name = "Alice";
const greeting = `Hello, ${name}!`;
Anything inside ${} gets evaluated as JavaScript—variables, calculations, function calls:
const price = 19.99;
const quantity = 3;
const message = `Total: $${price * quantity}`; // "Total: $59.97"
Multi-line strings also become simple. Just write across multiple lines and the breaks are preserved:
const message = `Line one
Line two
Line three`;
Tagged templates let you process the string through a function before getting the final result.
You write a function name directly before the backtick (no parentheses, no space).
That function can inspect and modify each part of the string—useful when you need to make user input safe to display on a webpage:
function escapeHtml(strings, ...values) {
const escaped = values.map(v => String(v)
.replace(/</g, "<")
.replace(/>/g, ">"));
return strings.reduce((result, str, i) =>
result + str + (escaped[i] || ""), "");
}
const userInput = "<script>alert('hacked')</script>";
const safe = escapeHtml`User said: ${userInput}`;
// "User said: <script>alert('hacked')</script>"
20. Explain the difference between spread syntax and rest parameters
This question checks whether you understand these two features that use the same … syntax but do opposite things.
How to answer
Spread syntax expands a collection into individual pieces. Rest parameters collect individual pieces into a collection.
They look identical but work in opposite directions.
Spread pulls items out of an array or object. Use it when you want to copy, combine, or pass array items as separate arguments:
const numbers = [1, 2, 3];
console.log(...numbers); // 1 2 3 (three separate values, not an array)
const fruits = ["apple", "banana"];
const moreFruits = ["orange", ...fruits, "grape"];
// ["orange", "apple", "banana", "grape"]
const defaults = { theme: "light", fontSize: 14 };
const userSettings = { fontSize: 18 };
const settings = { ...defaults, ...userSettings };
// { theme: "light", fontSize: 18 }
Rest gathers separate values into an array.
Use it when you want a function to accept any number of arguments, or when destructuring to capture “everything else”:
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2); // 3
sum(1, 2, 3, 4); // 10
const [first, ...remaining] = [1, 2, 3, 4, 5];
// first = 1, remaining = [2, 3, 4, 5]
The easy way to remember this: spread unpacks, rest packs.
21. What is a closure and how can it be used to create private state?
This question tests whether you understand how functions remember variables from their surrounding scope—a concept that’s fundamental to JavaScript.
How to answer
A closure is a function that remembers the variables from the place where it was created, even after that outer code has finished running.
Here’s a simple example:
function createCounter() {
let count = 0;
return function() {
count += 1;
return count;
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3
When createCounter runs, it creates a variable count and returns an inner function.
Normally, count would disappear after createCounter finishes. But the inner function keeps a reference to it—that’s the closure.
Every time you call counter(), it accesses and updates that same count variable.
This is useful when you want to hide a variable so that outside code can’t access or change it directly.
Closures appear everywhere in modern JavaScript tooling, including TypeScript, where type annotations help clarify what each function captures. Interviewers often ask about them because they reveal whether you think like a software engineer who understands how memory and function scope really behave.
In the example above, there’s no way to reach count from outside—you can only interact with it through the returned function. This lets you control exactly how the data can be read or modified:
function createBankAccount(initial) {
let balance = initial;
return {
deposit(amount) { balance += amount; },
withdraw(amount) { balance -= amount; },
getBalance() { return balance; }
};
}
const account = createBankAccount(100);
account.deposit(50);
account.getBalance(); // 150
// No way to access 'balance' directly or set it to whatever you want
22. How do you use destructuring to unpack arrays and objects?
This question tests whether you know modern JavaScript syntax for extracting values from data structures.
How to answer
Destructuring lets you pull values out of arrays and objects into separate variables in a single line, instead of accessing each one individually.
With arrays, you use square brackets, and the position determines which value goes where:
const rgb = [255, 128, 0];
// Without destructuring
const red = rgb[0];
const green = rgb[1];
const blue = rgb[2];
// With destructuring
const [red, green, blue] = rgb;
With objects, you use curly braces and match the property names:
const user = { name: "Alice", age: 30, city: "Paris" };
// Without destructuring
const name = user.name;
const age = user.age;
// With destructuring
const { name, age } = user;
You can also set default values in case something is missing:
const { name, country = "Unknown" } = user;
// country is "Unknown" because user doesn't have that property
And you can rename variables as you destructure:
const { name: userName, age: userAge } = user;
// Creates variables called userName and userAge
Destructuring works in function parameters too, which is common in React:
function greet({ name, age }) {
return `Hello ${name}, you are ${age}`;
}
greet(user); // "Hello Alice, you are 30"
23. Implement a debounce function and explain why it improves performance.
This question tests whether you can write practical utility functions and understand common performance patterns.
How to answer
Debouncing limits how often a function runs.
If something triggers repeatedly—like a user typing in a search box—debouncing waits until the activity stops before running the function.
Without debouncing, a search input might fire an API request on every keystroke. Type “hello” and you send 5 requests.
With debouncing, the request only fires after the user stops typing for a moment—one request instead of five.
Here’s how it works:
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
Each time the returned function is called, it cancels any pending timer and starts a new one.
While the original function only runs if the timer completes without being cancelled—meaning no new calls happened during the delay.
How to use it:
const searchAPI = (query) => {
console.log("Searching for:", query);
};
const debouncedSearch = debounce(searchAPI, 300);
// User types "hello" quickly
debouncedSearch("h");
debouncedSearch("he");
debouncedSearch("hel");
debouncedSearch("hell");
debouncedSearch("hello");
// Only "hello" gets logged, after 300ms pause
This improves performance by reducing unnecessary work. Instead of firing a network request on every keystroke or recalculating layout on every pixel of a resize, you wait until the user pauses.
A lot of performance tuning in JavaScript comes down to simple algorithms—doing less work, doing it later, or doing it only when needed. Debouncing fits this pattern perfectly.
Search inputs, window resize handlers, and scroll listeners all benefit from this pattern.
24. What are the differences between synchronous and asynchronous functions?
This question tests whether you understand how JavaScript handles operations that take time, like fetching data or reading files.
How to answer
Synchronous code runs line by line, where each line waits for the previous one to finish. Asynchronous code starts something and moves on while it completes in the background.
With synchronous code, if something takes a long time, everything else stops and waits:
console.log("First");
console.log("Second"); // Waits for "First" to finish
console.log("Third"); // Waits for "Second" to finish
With asynchronous code, JavaScript registers the slow task, continues with other code, and comes back to handle the result when it’s ready:
console.log("First");
setTimeout(() => console.log("Second"), 1000);
console.log("Third");
// Output:
// "First"
// "Third"
// "Second" (after 1 second)
There are three main ways to write async code:
- Callbacks are functions you pass to run when the async work finishes. They work but tend to get messy when you chain multiple operations.
- Promises represent a value that will exist in the future. You use .then() to handle success and .catch() for errors, and you can chain multiple operations together.
- Async/await makes promises look like regular synchronous code. You mark a function as async and use await to pause until a promise resolves. It’s still using promises under the hood—just easier to read and write.
25. What are higher-order functions, and how can you use them to build custom React hooks?
This question tests whether you understand functions that work with other functions—a pattern used constantly in JavaScript and React.
How to answer
A higher-order function is a function that either takes another function as an argument, returns a function, or both.
You’ve already used them if you’ve used map, filter, or forEach—they all take a function as an argument.
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2); // map takes a function
// [2, 4, 6, 8]
In React, custom hooks use this pattern to package up reusable logic.
A hook is just a function that can use other hooks and return whatever you need—values, functions, or both:
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = () => setValue(v => !v);
return [value, toggle];
}
// Using it in a component
function Menu() {
const [isOpen, toggleOpen] = useToggle(false);
return <button onClick={toggleOpen}>{isOpen ? "Close" : "Open"}</button>;
}
The useToggle hook packages up the state and the toggle logic so you don’t have to rewrite it every time you need a boolean that flips back and forth.
You can create hooks for any reusable logic—fetching data, form handling, localStorage, timers, and more.
DOM (Document Object Model) and browser APIs front-end interview questions
These questions test your understanding of how JavaScript interacts with the page and the browser environment.
The DOM is how browsers represent your HTML as objects that JavaScript can read and modify—every element, attribute, and piece of text becomes something your code can access and change.
26. How does event delegation work, and why is it useful?
This question tests whether you know how to handle events efficiently, especially when dealing with many elements or elements that get added dynamically.
How to answer
Event delegation means attaching one event listener to a parent element instead of attaching separate listeners to each child.
When something happens on a child, the event bubbles up to the parent, where your single listener catches it.
Say you have a list with 100 items and want to handle clicks on each one. You could attach 100 listeners:
// Inefficient — 100 separate listeners
document.querySelectorAll('li').forEach(item => {
item.addEventListener('click', handleClick);
});
Or you could attach one listener to the parent and check which child was clicked:
// Efficient — 1 listener handles all items
document.querySelector('ul').addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
handleClick(event);
}
});
This is useful for three reasons:
- Performance: One listener uses less memory than hundreds
- Dynamic elements: New items added to the list automatically work without attaching new listeners
- Simpler cleanup: You only have one listener to remove
The key concept is event bubbling: when you click a child element, that click event travels up through all its ancestors. The parent can intercept it along the way.
27. Why use addEventListener() instead of setting element.onclick?
This question checks whether you understand the different ways to attach event handlers and their tradeoffs.
How to answer
addEventListener() is more flexible than setting onclick directly. The main difference is that addEventListener() lets you attach multiple handlers to the same event, while onclick can only hold one.
// With onclick, the second handler replaces the first
button.onclick = () => console.log("First");
button.onclick = () => console.log("Second");
// Only "Second" runs when clicked
// With addEventListener, both handlers run
button.addEventListener("click", () => console.log("First"));
button.addEventListener("click", () => console.log("Second"));
// Both "First" and "Second" run when clicked
addEventListener() also gives you more control:
- You can remove specific handlers with removeEventListener().
- You can choose whether to handle events during the capturing phase (going down) or bubbling phase (going up).
- You can use options like once to auto-remove after one use, or passive to improve scroll performance.
For simple cases, either approach works, but addEventListener() is the standard because it handles everything without surprises.
28. Describe event propagation (capturing and bubbling) and how to stop it.
This question tests whether you understand how events travel through the page, which is important for debugging unexpected behavior and building complex UIs.
How to answer
When you click an element, the event doesn’t just fire on that element. It travels through the page in two phases.
- First is the capturing phase: the event starts at the top of the document and travels down through each ancestor until it reaches the element you clicked.
- Then is the bubbling phase: the event travels back up from the element through its ancestors to the top.
By default, event listeners fire during the bubbling phase. T
hat’s why clicking a button inside a div can trigger handlers on both—the button’s handler fires, then the event bubbles up and triggers the div’s handler too.
// Both handlers fire when you click the button
document.querySelector("div").addEventListener("click", () => {
console.log("Div clicked");
});
document.querySelector("button").addEventListener("click", () => {
console.log("Button clicked");
});
// Click the button, output:
// "Button clicked"
// "Div clicked"
Sometimes you might want to stop this.
event.stopPropagation() prevents the event from traveling further:
document.querySelector("button").addEventListener("click", (event) => {
event.stopPropagation(); // Stops here, won't reach the div
console.log("Button clicked");
});
If you need to listen during the capturing phase instead, pass { capture: true } as a third argument to addEventListener().
29. What is the IntersectionObserver API and how does it enable lazy loading?
This question tests whether you know modern browser APIs for detecting when elements become visible.
How to answer
IntersectionObserver watches elements and tells you when they enter or leave the viewport—the visible area of the page.
You create an observer with a callback function, then tell it which elements to watch:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log("Element is now visible");
}
});
});
observer.observe(document.querySelector(".my-element"));
For lazy loading images, you start with placeholder sources and swap in the real image when it becomes visible:
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
imageObserver.unobserve(img);
}
});
});
Images below the fold don’t load until the user scrolls near them, which speeds up the initial page load and saves bandwidth.
Accessibility and semantic HTML front-end interview questions
These questions test whether you can build interfaces that work for everyone, including people using screen readers, keyboards, or other assistive technologies.
30. Why should interactive elements like buttons use semantic HTML rather than <div>s?
This question checks whether you understand the hidden functionality that comes with proper HTML elements.
How to answer
A <button> gives you a lot for free: keyboard users can tab to it and press Enter or Space to activate it, and screen readers announce it as a button so users know it’s interactive.
A <div> has none of this. If you style it to look like a button and add a click handler, mouse users won’t notice a difference.
But keyboard users can’t reach it, and screen reader users won’t know it’s clickable.
<!-- Missing accessibility features -->
<div class="button" onclick="doSomething()">Click me</div>
<!-- Works for everyone -->
<button onclick="doSomething()">Click me</button>
You can add tabindex, role=”button“, and keyboard handlers to a div, but you’re recreating what <button> provides automatically—and you’ll probably miss something.
31. What is the difference between aria-label and aria-labelledby?
This question tests whether you understand how to make elements accessible when the visual design doesn’t include visible labels.
How to answer
Both give an element an accessible name—the text screen readers announce. The difference is where that text comes from.
aria-label sets the name directly as a string:
<button aria-label="Close menu">✕</button>
The button shows an X, but screen readers announce “Close menu button.”
aria-labelledby points to another element on the page:
<h2 id="cart-title">Shopping Cart</h2>
<div role="region" aria-labelledby="cart-title">
<!-- cart contents -->
</div>
The region’s accessible name comes from whatever text is inside the h2.
If you later change the h2 to say “Your Cart (3 items)”, the screen reader automatically uses that new text.
Quick summary:
- Use aria-label when there’s nothing visible to reference—like a button that only shows an icon.
- Use aria-labelledby when the label is already visible somewhere on the page, so you don’t have to write the same text twice.
32. How do you manage focus and keyboard navigation in a single-page React app?
This question tests whether you understand accessibility challenges in apps where the page doesn’t fully reload.
How to answer
In traditional websites, clicking a link loads a new page, and focus moves to the top.
In single-page apps, the content swaps, but the page doesn’t reload—screen reader users have no indication that anything changed, and focus might stay on an element that no longer exists.
To fix this, use JavaScript to place the cursor on a specific element after navigation. This tells screen readers where to start reading and lets keyboard users continue from a logical spot:
useEffect(() => {
document.querySelector("h1")?.focus();
}, [location.pathname]);
Finally, keyboard users need a few more things to navigate comfortably:
- A “skip to main content” link at the top of the page, so they can jump straight to the content instead of tabbing through every navigation item.
- Focus trapped inside modals—pressing Tab shouldn’t move to elements hidden behind the modal
- Focus returned to the button that opened a modal when it closes, so users aren’t left wondering where they are on the page.
33. How would you suggest testing accessibility? Suggest one automated method and one manual.
This question checks whether you know how to verify that your site works for people using assistive technologies.
How to answer
You need both automated and manual testing because they catch different problems.
Automated tools scan your code and flag common issues like missing alt text, low color contrast, or form inputs without labels:
- WAVE Evaluation Tool is a good example—it’s a browser extension that lists violations and explains how to fix them.
- Lighthouse in Chrome DevTools also includes accessibility audits.
These tools are fast, but they can only check things that can be measured programmatically.
Manual testing means using your site the way someone with a disability would:
- The simplest test: unplug your mouse and try to do everything with the keyboard.
- Press Tab to move between elements, Enter or Space to activate buttons, Escape to close modals.
- If you get stuck or can’t tell what’s focused, that’s a problem.
For a deeper check, you can also turn on a screen reader—VoiceOver on Mac or NVDA on Windows—and navigate your site with your eyes closed.
Does it make sense? Can you tell what each element does? Automated tools can’t answer these questions.
Performance and optimization front-end interview questions
These questions test whether you understand what makes websites fast or slow and how to improve load times.
34. What is lazy loading, and how do you implement it for images and React components?
This question tests whether you know how to speed up page loads by deferring things that aren’t immediately needed.
How to answer
Lazy loading means waiting to load something until it’s actually needed. Instead of downloading everything upfront, you load it when the user is about to see or use it.
For images, the simplest approach is the loading=”lazy” attribute:
<img src="photo.jpg" alt="A photo" loading="lazy">
The browser handles everything—images below the fold won’t load until the user scrolls near them.
- For more control, you can use IntersectionObserver to detect when an image enters the viewport and swap in the real source at that moment.
- For React components, you use React.lazy() combined with Suspense. This splits your code so that components only download when they’re rendered:
const HeavyChart = React.lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
<Suspense fallback={<p>Loading chart...</p>}>
<HeavyChart />
</Suspense>
);
}
The HeavyChart component and all its dependencies stay in a separate file.
Users who never visit the dashboard never download that code. The fallback prop shows something while the component loads.
Both techniques improve initial load times—users see content faster because they’re not waiting for things they haven’t scrolled to yet or features they might not use.
35. Explain the browser’s event loop and why misusing microtasks can block interactions.
This question tests whether you understand how JavaScript executes code, whicis h important for debugging timing issues.
How to answer
JavaScript runs on a single thread, meaning it can only do one thing at a time. The event loop is the system that decides what runs next.
- When your code runs, functions execute on the call stack one at a time. But not everything runs immediately.
- When you use setTimeout, the callback goes into a waiting area called the task queue.
- When you use Promises, their callbacks go into a different waiting area called the microtask queue.
The event loop follows a simple rule: finish whatever’s on the call stack, then run everything waiting in the microtask queue, then run the next item from the task queue.
Microtasks always get priority.
console.log("First");
setTimeout(() => console.log("Second"), 0);
Promise.resolve().then(() => console.log("Third"));
console.log("Fourth");
// Output: "First", "Fourth", "Third", "Second"
Even with a 0ms delay, the setTimeout waits for the Promise because microtasks run first.
This becomes a problem if you chain too many Promises or keep adding microtasks from within microtasks.
The browser can’t handle clicks, update the screen, or do anything else until the microtask queue empties. Your page freezes even though JavaScript is technically running.
36. How do service workers improve performance and enable offline functionality?
This question tests whether you understand how to make web apps work without a constant network connection.
How to answer
A service worker is a JavaScript file that runs separately from your webpage.
It sits between your site and the network, intercepting requests and deciding how to respond—with cached files, network data, or a mix of both.
You register a service worker like this:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
Inside the service worker, you can cache files during installation and serve them when requests come in.
If a user loads your site and their connection drops, the service worker responds from the cache instead of failing.
This improves performance because cached files load instantly—no waiting for the network—and enables offline access because the cache works without a connection.
Keep in mind this one requirement: service workers only work over HTTPS (except localhost for testing). They’re powerful enough to intercept all requests, so browsers only allow them on secure connections.
37. What strategies help optimize the critical rendering path?
This question tests whether you understand what happens between requesting a page and seeing content on screen.
How to answer
The critical rendering path is everything the browser does before it can show your page: download files, parse HTML and CSS, run JavaScript, calculate layout, and paint pixels.
Speeding this up means users see content sooner.
A few strategies can help:
- Shrink your files. Minify CSS and JavaScript to remove unnecessary characters. Enable compression (Gzip or Brotli) on your server. Smaller files download faster.
- Load CSS early, JavaScript late. The browser won’t display anything until CSS is processed—it needs to know how things look. Put CSS in the <head>. JavaScript blocks HTML parsing, so put scripts at the end of <body> or use defer.
- Defer what you don’t need immediately. Lazy-load images below the fold. Split JavaScript so only essential code loads first. Use preload for important resources and preconnect to start connections to external domains early.
- Optimize images. They’re often the largest files on a page. Use modern formats like WebP that compress better. Serve smaller sizes on mobile so phones don’t download desktop-sized images.
The goal is to show something useful as fast as possible, then load the rest in the background.
Security and best practices front-end interview questions
These questions test whether you understand common web security threats and how to protect your applications from attacks.
38. Explain the Same-Origin Policy and give an example where CORS is necessary.
This question checks whether you understand how browsers protect users from malicious websites.
How to answer
The Same-Origin Policy is a browser security rule that prevents code on one website from reading data from a different website.
Two pages have the same “origin” if they share the same protocol (http or https), domain, and port number:
- Code running on https://mysite.com can’t make a request to https://api.othersite.com and read the response.
- Without this rule, a malicious site could steal your data from any site you’re logged into—your email, bank account, anything.
CORS (Cross-Origin Resource Sharing) is how servers opt out of this restriction when they want to allow cross-origin requests.
When your React app on localhost:3000 needs to call an API on api.example.com, the browser blocks it by default.
But if the API server includes the header Access-Control-Allow-Origin: http://localhost:3000 in its response, the browser allows it.
Your front‑end and back‑end often run on different hosts or ports, especially in development. CORS lets them talk to each other safely.
39. How does a Content Security Policy (CSP) mitigate XSS and clickjacking?
This question tests whether you know how to add an extra layer of defense against code injection attacks.
How to answer
A Content Security Policy is a set of rules you send as an HTTP header that tells the browser which resources your page is allowed to load.
Anything not on the list gets blocked.
This defends against XSS (Cross-Site Scripting), where attackers inject malicious scripts into your page.
With a strict CSP, you can block all inline scripts and only allow scripts from your own domain using Content-Security-Policy: script-src ‘self’.
Even if an attacker finds a way to inject <script>alert(‘hacked’)</script> into your HTML, the browser refuses to run it because inline scripts aren’t on the allowed list.
CSP also prevents clickjacking, where attackers hide your site inside an invisible iframe on a malicious page to trick users into clicking things they didn’t intend to.
You block this by telling browsers not to let other sites frame yours: Content-Security-Policy: frame-ancestors ‘none’.
Remember, CSP works as a backup defense. It doesn’t replace proper input validation and output encoding, but if those fail, CSP can still stop the attack.
40. What precautions should you take when rendering user-generated content in React?
This question checks whether you know how to display content from users without creating security vulnerabilities.
How to answer
User-generated content—comments, profile bios, forum posts—can contain malicious code.
If you display it carelessly, attackers can inject scripts that steal data or hijack accounts.
React helps by default.
When you render content with JSX like <p>{userComment}</p>, React automatically escapes special characters.
A string like <script>alert(‘hacked’)</script> displays as literal text instead of running as code.
The danger, however, comes from dangerouslySetInnerHTML, which renders raw HTML without any escaping:
// Dangerous - executes any scripts in userContent
<div dangerouslySetInnerHTML={{ __html: userContent }} />
Sometimes you need it—for a rich text editor that saves HTML, for example.
In those cases, sanitize the content first with a library like DOMPurify:
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userContent);
<div dangerouslySetInnerHTML={{ __html: clean }} />
DOMPurify strips out dangerous tags like <script> and event handlers like onclick while keeping safe formatting like <strong> and <em>.
Tooling and workflow front-end interview questions
These questions test whether you know the tools and processes that make front-end development efficient and collaborative.
41. Describe your preferred Git workflow for a front-end project.
This question checks whether you can work effectively in a team using version control.
How to answer
A typical workflow uses feature branches and pull requests to keep the main codebase stable.
For each feature or bug fix, you create a new branch from main: git checkout -b feature/add-search.
This isolates your work until it’s ready.
- While working, commit often with clear messages that explain what changed and why—small, focused commits are easier to review and easier to revert if something breaks.
- When you’re done, push your branch and open a pull request. Someone else reviews your code, catches potential issues, suggests improvements, and then approves the merge.
- After merging, delete the feature branch to keep the repository tidy. Some teams also tag specific commits for releases (v1.0.0) so they can track exactly what’s deployed.
42. Compare npm, Yarn, and pnpm, and explain the purpose of lockfiles.
This question tests whether you understand the tools that manage JavaScript dependencies.
How to answer
All three are package managers that install libraries from the npm registry, but they take different approaches:
- npm comes bundled with Node.js, making it the default choice for most projects.
- Yarn was created by Facebook to fix speed and reliability issues npm had at the time—it introduced parallel downloads and lockfiles. npm has caught up since, but some teams still prefer Yarn.
- pnpm takes a different approach entirely: it stores each package version once on your machine and links to it from every project that needs it. If ten projects use React 18, pnpm keeps one copy instead of ten, saving disk space and speeding up installs.
Whichever you choose, the lockfile is what matters most.
Each manager creates one (package-lock.json, yarn.lock, or pnpm-lock.yaml) that records the exact version of every package and its dependencies. Without it, running npm install on two machines might pull slightly different versions, leading to bugs that only appear on some computers.
43. How would you configure a bundler for code splitting and tree shaking?
This question tests whether you know how to optimize JavaScript bundles for faster page loads.
How to answer
Bundlers like Webpack and Vite combine your JavaScript files into bundles that browsers download.
Without optimization, users download everything upfront—even code for pages they’ll never visit.
Code splitting and tree shaking solve this.
Code splitting divides your app into smaller bundles that load only when needed. You create a split using dynamic imports:
const Settings = React.lazy(() => import('./Settings'));
The bundler sees this and creates a separate file for Settings. That file only downloads when someone actually visits the settings page.
In Webpack, the optimization.splitChunks setting takes this further by pulling shared libraries like React into their own bundle, which browsers can cache separately from your app code.
Tree shaking removes unused code.
Say you import one function from a utility library that exports fifty functions. Tree shaking keeps the one you use and discards the rest.
A few things need to be in place for these to work:
- Use ES modules (import/export) instead of CommonJS (require)—bundlers can only analyze what’s safe to remove with ES module syntax.
- Build in production mode—development builds skip optimizations to stay fast.
- Check your actual bundle sizes in production, not development.
Bonus: React interview questions
If you’re interviewing for a role that uses React, you’ll face questions on hooks, state management, concurrent rendering, and Server Components.
- We cover all of that in a dedicated guide: 30 Modern React Interview Questions for All Levels.
- You can also explore this list of practical React projects for beginners (with source code).
Nail your front-end job interview with Mimo
Ready to put this knowledge into practice?
Mimo’s Front-End Development career path teaches you HTML, CSS, JavaScript, and React through hands-on projects—the exact same skills interviewers test for.
Here’s what you get:
- Interactive lessons that build your skills step by step
- Real projects for your GitHub portfolio
- Practice with the patterns and concepts covered in this guide
- A professional certificate recognized by employers
- Access to an AI assistant, a code editor, and an AI app builder
