HTML Interview Questions and Answers
Short answer
HTML rounds test whether you write structured, accessible markup rather than a page of divs. The recurring questions are semantic elements, async versus defer, how the browser parses HTML into a render tree, accessible forms, responsive images, and which meta tags matter.
| The question | What it reveals |
|---|---|
| What is semantic HTML? | Whether you have ever used a screen reader or worked on SEO |
| async vs defer | Whether you understand parser blocking, not just the keywords |
| What is the DOM? | Whether "the DOM" and "the HTML" are separate things in your head |
| How do you label a form field? | Whether accessibility is habit or an afterthought |
| How do you serve responsive images? | Whether you have shipped for real Indian mobile networks |
| Why does the page jump while loading? | Whether you can connect markup to Core Web Vitals |
| When would you add an ARIA role? | Seniority - the right answer is usually "I wouldn't" |
| div vs section vs article | Whether you can justify structure, not recite a list |
How a frontend interview actually uses HTML questions
HTML is the round candidates prepare for least, which is exactly why interviewers keep it. Everyone can write a div and a button, so nobody is trying to catch you out on syntax. What they are looking for is whether your markup carries meaning, whether accessibility is something you do by default or something you would add later if there were time, and whether you know what the browser does with what you wrote.
That changes how you should answer. "Semantic HTML means using header, nav, main and footer" is a definition and earns very little. "Semantic HTML means the element says what the content is, so a screen reader user can jump between landmarks instead of arrowing through the whole page, and so I get keyboard behaviour and focus handling for free" is the same knowledge attached to a consequence, and it is what gets marked.
What is semantic HTML and why does it matter?
A semantic element describes what its content is rather than how it should look. The practical payoff is that assistive technology, search crawlers and the browser itself all read that meaning. Screen readers expose landmarks so a user can jump straight to the main content; a `button` is focusable, fires on Enter and Space, and is announced as a button, none of which a clickable div does without work.
1<!-- Div soup: valid, renders identically, communicates nothing -->
2<div class="header">
3 <div class="nav"><div class="link" onclick="go()">Kits</div></div>
4</div>
5<div class="content">
6 <div class="title">React Interview Kit</div>
7 <div class="btn" onclick="buy()">Buy now</div>
8</div>
9<!-- → screen reader announces: nothing useful. Tab key skips both controls. -->
10
11<!-- Semantic: same layout, real behaviour -->
12<header>
13 <nav aria-label="Main"><a href="/kits">Kits</a></nav>
14</header>
15<main>
16 <h1>React Interview Kit</h1>
17 <button type="button" onclick="buy()">Buy now</button>
18</main>
19<!-- → landmarks: banner, navigation, main. Both controls are tabbable, -->
20<!-- → the button fires on Enter AND Space, and is announced as a button. -->The follow-up is usually about `div` versus `section` versus `article`, and the rule is simpler than most answers make it. Use `article` when the content would still make sense lifted out of the page, a blog post, a product card, a comment. Use `section` for a thematic grouping that has a heading. Use `div` when you need a box purely to hang layout or styles on, which is a perfectly good reason and not something to apologise for. A `section` without a heading is usually a `div` that someone renamed to feel more semantic.
What is the difference between async and defer?
A plain `script` tag blocks the parser: the browser stops building the DOM, fetches the file, runs it, and only then continues. Both `async` and `defer` remove the fetch from that critical path by downloading in parallel. They differ in when the script executes and whether order is guaranteed.
1<!-- Blocks parsing at this line until fetched AND executed -->
2<script src="/blocking.js"></script>
3<!-- → worst case for LCP; the DOM below this line does not exist yet -->
4
5<!-- Downloads in parallel, runs the moment it lands, order NOT guaranteed -->
6<script src="/analytics.js" async></script>
7<!-- → right for independent scripts: analytics, error reporting, ads -->
8
9<!-- Downloads in parallel, runs after parsing, in document order -->
10<script src="/vendor.js" defer></script>
11<script src="/app.js" defer></script>
12<!-- → vendor.js always runs before app.js, both before DOMContentLoaded -->
13
14<!-- Modules are deferred by default -->
15<script type="module" src="/app.mjs"></script>
16<!-- → no defer attribute needed; add async to opt out of ordering -->The way to make this answer land is to give the decision rule rather than the definitions. Use `defer` for anything that touches the DOM or depends on another script, which is almost all application code. Use `async` only for genuinely independent third-party scripts where running early matters more than running in order. And note that `async` on a script that expects the DOM to exist is a real bug that appears intermittently, because whether it breaks depends on how fast the file downloaded.
Does putting a script at the end of body do the same thing as defer?
Almost, but not quite. A script at the end of the body still does not start downloading until the parser reaches it, so on a slow connection you have wasted the whole parse time before the request even leaves. A deferred script in the head starts downloading immediately and still executes after parsing. The end-of-body placement was the pre-defer workaround; defer is strictly better now.
How does the browser turn HTML into pixels?
The browser parses HTML into the DOM and CSS into the CSSOM, combines them into a render tree of everything that will actually be painted, computes the geometry of each box in layout, paints those boxes into layers, and composites the layers to the screen. The question is usually phrased as "what is the critical rendering path", and the point of it is to see whether you know why a stylesheet in the head blocks rendering while an image does not.
The distinction interviewers most want is between the HTML and the DOM. The HTML is the text the server sent; the DOM is the live tree of objects the browser built from it and has been mutating ever since. They match at first paint and diverge the moment any script runs, which is why "view source" and the Elements panel show different things, and why an SEO crawler that does not execute JavaScript sees the first one.
1// DOMContentLoaded: HTML parsed, deferred scripts done. Images may still load.
2document.addEventListener("DOMContentLoaded", () => {
3 console.log("tree ready:", document.querySelectorAll("*").length);
4 // → tree ready: 342
5});
6
7// load: everything, including images, stylesheets and iframes
8window.addEventListener("load", () => console.log("all assets in"));
9// → all assets in can be seconds later on a 3G connection
10
11// The HTML the server sent vs the DOM as it is now
12document.body.append(document.createElement("aside"));
13console.log(document.querySelectorAll("aside").length);
14// → 1 present in the DOM, absent from view-sourceHow do you build an accessible form?
Forms are where HTML questions get specific, because a form is the one place bad markup has an immediate, demonstrable cost. Every input needs a real label associated with it, related controls belong in a fieldset with a legend, and the browser's own validation and autofill only work if you use the right type and autocomplete attributes.
1<form action="/signup" method="post">
2 <label for="email">Email</label>
3 <input
4 id="email"
5 name="email"
6 type="email"
7 autocomplete="email"
8 required
9 aria-describedby="email-hint"
10 />
11 <p id="email-hint">We send the kit link here.</p>
12 <!-- → announced as: "Email, edit text, required. We send the kit link here." -->
13
14 <fieldset>
15 <legend>Which kit?</legend>
16 <label><input type="radio" name="kit" value="js" /> JavaScript</label>
17 <label><input type="radio" name="kit" value="react" /> React</label>
18 </fieldset>
19 <!-- → the legend is read with each option, so "JavaScript" has context -->
20
21 <button type="submit">Continue</button>
22 <!-- → type="submit" is the default inside a form; type="button" is not, -->
23 <!-- → which is the bug behind "my button reloads the page" -->
24</form>Three details are worth volunteering. A `label` wrapping the input works without a `for` attribute, but the explicit `for` and `id` pairing is more robust and is what testing tools check. `type="email"` and `type="tel"` change the on-screen keyboard on mobile, which is a real usability win on an India-first product where most traffic is phones. And a placeholder is not a label, it disappears the moment the user types, and screen readers treat it inconsistently.
This is 1 of 200+ questions in the Complete Frontend Kit
Get every question with detailed answers, follow-ups and real code, plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the Complete Frontend Kit → ₹499How do you serve responsive images without hurting CLS?
Two separate problems get conflated here. Serving the right file size is `srcset` and `sizes`, which let the browser pick a source based on the viewport and pixel density. Preventing layout shift is `width` and `height` attributes, which let the browser reserve the correct box before the image arrives, the single highest-impact fix for Cumulative Layout Shift.
1<img
2 src="/kit-800.jpg"
3 srcset="/kit-400.jpg 400w, /kit-800.jpg 800w, /kit-1600.jpg 1600w"
4 sizes="(max-width: 600px) 100vw, 50vw"
5 width="800"
6 height="450"
7 loading="lazy"
8 decoding="async"
9 alt="React Interview Kit cover"
10/>
11<!-- → on a 360px phone the browser downloads kit-400.jpg, not kit-1600.jpg -->
12<!-- → width and height reserve a 16:9 box immediately, so CLS stays at 0 -->
13
14<picture>
15 <source media="(max-width: 600px)" srcset="/hero-square.jpg" />
16 <img src="/hero-wide.jpg" width="1200" height="600" alt="" />
17</picture>
18<!-- → picture is for DIFFERENT crops; srcset is for the same image resized -->
19<!-- → alt="" marks it decorative, so screen readers skip it entirely -->The trap in this question is `loading="lazy"` on the hero image. Lazy loading defers the request until the image is near the viewport, which is exactly wrong for the largest element above the fold, it delays the Largest Contentful Paint rather than improving it. Lazy-load what is below the fold, and use `fetchpriority="high"` on the one image that is the LCP element.
What are data attributes for?
A `data-*` attribute stores custom data on an element in a way that is valid HTML and reachable from both JavaScript and CSS. It is the correct answer to "where do I put the id for this row" when the alternative is a class name that something else might match, or an invented attribute that will not validate.
1<button data-kit-id="react-kit" data-price="599">Buy</button>
2
3<style>
4 /* Attribute selectors work on data-* like any other attribute */
5 [data-state="loading"] { opacity: 0.5; pointer-events: none; }
6 button::after { content: " ₹" attr(data-price); }
7 /* → renders as: Buy ₹599 */
8</style>
9
10<script>
11 const el = document.querySelector("button");
12 console.log(el.dataset.kitId);
13 // → react-kit data-kit-id becomes dataset.kitId (camelCase)
14 el.dataset.state = "loading";
15 // → adds data-state="loading", and the CSS rule above applies
16</script>Two caveats make the answer complete. Everything in `dataset` is a string, so `data-price` comes back as "599" and needs converting before arithmetic. And in a React or Vue application, most state should live in the component rather than on the DOM node, data attributes are for the seam between markup and script, such as analytics hooks and end-to-end test selectors, not a place to store application state.
When should you add an ARIA role?
Usually never, and being able to say that confidently is the strongest possible answer. The first rule of ARIA is not to use ARIA: if a native element does the job, it already carries the role, the keyboard behaviour and the focus handling, and an ARIA attribute only ever changes what assistive technology announces, it adds no behaviour at all.
1<!-- Wrong: role and tabindex reimplementing a button, badly -->
2<div role="button" tabindex="0" onclick="buy()">Buy</div>
3<!-- → still does not fire on Space, has no disabled state, no form submit -->
4
5<!-- Right: the native element -->
6<button type="button" onclick="buy()">Buy</button>
7<!-- → focusable, Enter and Space, disabled, announced as a button -->
8
9<!-- Redundant: these roles are already implied -->
10<nav role="navigation"> ... </nav>
11<!-- → no benefit; some screen readers announce it twice -->
12
13<!-- Legitimate: a live region has no native equivalent -->
14<div role="status" aria-live="polite">Payment received</div>
15<!-- → announced when the text changes, without moving focus -->Keep one real example ready for when the interviewer pushes, because "just use native elements" alone can sound like avoidance. Live regions are the cleanest one: there is no HTML element that means "announce this when it changes without stealing focus", so `aria-live` genuinely fills a gap. Tabs, combo-boxes and tree views are the others, and the honest framing is that those are hard enough that reaching for a tested library is the senior decision. Deeper accessibility questions have their own round, see the accessibility page linked below.
localStorage, sessionStorage or a cookie?
`localStorage` persists until it is explicitly cleared and is shared across every tab on the origin. `sessionStorage` is scoped to one tab and dies when that tab closes. Cookies are small, around 4KB, and are attached to every matching request, which is both their purpose and their cost.
The interview answer that matters is about auth tokens, and it is that neither storage API is a good place for one. Both are readable by any JavaScript on the page, so a single cross-site scripting bug hands over the token. An `httpOnly`, `Secure`, `SameSite` cookie is unreadable from script and is sent automatically, which is why it remains the default for session credentials. Use `localStorage` for preferences - theme, dismissed banners, a draft, and `sessionStorage` for state that should not survive the tab, such as a multi-step form in progress.
Which meta tags actually matter?
Very few, and knowing which ones are obsolete is part of the answer. The viewport tag is the one that breaks a site outright by its absence: without it, mobile browsers lay the page out at a fictional width of around 980 pixels and then zoom out, so every media query you wrote evaluates against the wrong width and your responsive CSS simply never applies.
1<meta charset="utf-8" />
2<!-- → must be within the first 1024 bytes, or the browser re-parses -->
3
4<meta name="viewport" content="width=device-width, initial-scale=1" />
5<!-- → without this, mobile lays out at ~980px and every media query misses -->
6
7<title>HTML Interview Questions and Answers</title>
8<meta name="description" content="Shown in the search result, not a ranking factor." />
9<link rel="canonical" href="https://www.forgefrontend.com/learn/html-interview-questions" />
10
11<meta property="og:title" content="HTML Interview Questions" />
12<meta property="og:image" content="https://www.forgefrontend.com/og/html.png" />
13<!-- → controls the card when the link is pasted into WhatsApp or LinkedIn -->
14
15<!-- Obsolete: ignored by every major search engine since 2009 -->
16<meta name="keywords" content="html, interview" />Adding that `keywords` has been ignored for over a decade, and that `description` affects click-through rather than ranking, is the kind of detail that separates someone who has shipped a public site from someone who copied a head block from a template. The canonical tag is the other one worth naming, because it is how you tell a crawler which URL is the real one when the same page is reachable more than one way.
What loses marks in an HTML round
The failures here are consistent and easy to avoid once you have seen the list.
- →Reciting the list of semantic tags without ever naming a behaviour they produce.
- →A clickable div with an onclick, then not noticing it cannot be reached by keyboard.
- →Treating a placeholder as a label, which leaves the field unlabelled once the user types.
- →Adding role and aria-* to native elements that already imply them.
- →Images with no width and height, then being unable to explain the layout shift.
- →Saying localStorage is fine for a JWT because it is convenient.
- →Claiming HTML is not important, the interviewer chose to ask, so they disagree.
Frequently asked questions
- What is semantic HTML and why does it matter?
- Semantic elements describe what the content is rather than how it looks. It matters because assistive technology exposes landmarks so users can jump to the main content, native controls come with focus and keyboard behaviour built in, and crawlers use the structure to understand the page. The strong answer names a behaviour, not just the tags.
- What is the difference between async and defer?
- Both download the script in parallel without blocking the parser. async executes as soon as the file lands, so order between scripts is not guaranteed; defer executes after parsing finishes, in document order, before DOMContentLoaded. Use defer for application code and async only for independent third-party scripts.
- Is putting a script at the end of body the same as defer?
- Not quite. A script at the end of the body only starts downloading when the parser reaches it, so the fetch begins late. A deferred script in the head starts downloading immediately and still runs after parsing. End-of-body was the workaround before defer existed; defer is better in every case now.
- What is the difference between HTML and the DOM?
- The HTML is the text the server sent. The DOM is the live tree of objects the browser built from that text and has been changing ever since scripts started running. They match at first paint and diverge immediately afterwards, which is why view-source and the Elements panel do not agree.
- When should I use section, article or div?
- Use article when the content would still make sense on its own elsewhere, a post, a product card, a comment. Use section for a thematic group that has a heading. Use div when you only need a box for layout or styling. A section without a heading is almost always a div that has been renamed.
- How do I make a form accessible?
- Give every input a label associated by for and id, group related controls in a fieldset with a legend, use the right input type so mobile keyboards adapt, add autocomplete attributes so browser autofill works, and link hints or errors with aria-describedby. Placeholders are not labels.
- What is the difference between srcset and picture?
- srcset offers the same image at several sizes and lets the browser pick based on viewport and pixel density. picture is for art direction, genuinely different crops or formats at different breakpoints. If the image content is identical and only the resolution changes, srcset is the right tool.
- How do width and height attributes on an img prevent layout shift?
- The browser uses them to compute the aspect ratio and reserve a correctly sized box before the file arrives, so surrounding content never jumps when it loads. Omitting them is one of the most common causes of a poor Cumulative Layout Shift score, and the fix costs two attributes.
- Should I use loading="lazy" on all images?
- No. Lazy loading is right for images below the fold, but applying it to the hero image delays the request for the Largest Contentful Paint element and makes the metric worse. Load the LCP image eagerly, ideally with fetchpriority="high", and lazy-load everything further down.
- When should I use ARIA roles?
- As rarely as possible. If a native element carries the role, use it, ARIA changes only what is announced and adds no behaviour, so role="button" on a div still needs focus, keyboard and disabled handling written by hand. Legitimate uses are the patterns with no native equivalent, such as aria-live regions.
- Is it safe to store a JWT in localStorage?
- No. Anything in localStorage or sessionStorage is readable by any JavaScript running on the page, so one cross-site scripting bug leaks the token. Use an httpOnly, Secure, SameSite cookie, which script cannot read and the browser sends automatically. Keep localStorage for preferences and non-sensitive state.
- Are HTML questions actually asked in frontend interviews?
- Yes, and disproportionately at companies that care about accessibility, performance or SEO. They are cheap to ask and hard to bluff, because the follow-up is always a consequence rather than a definition. Candidates routinely over-prepare React and lose marks on a form-labelling question.
This is 1 of 200+ questions in the Complete Frontend Kit
Get every question with detailed answers, follow-ups and real code, plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the Complete Frontend Kit → ₹499