Skip to content

Frontend Development Online Assessment Questions & Answers

40 questions for the online assessment stage — 16 easy, 23 medium, 1 hard.

About the online assessment round

Most companies start with an online test: aptitude and reasoning, a few MCQs on core Frontend Development concepts, and one or two short hands-on tasks. It filters a large number of applicants, so speed and accuracy both count.

Tips for the online assessment round

  • Practise under a timer: about one minute per MCQ and 15-20 minutes per hands-on task.
  • Read every option before answering; tests often include two nearly correct choices.
  • Use a stable internet connection, keep your camera on and avoid switching tabs in proctored tests.
  • Attempt the questions you are sure about first, then return to the harder ones.
Solved — online assessmentStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

Compare SPA, MPA, server-side rendering and static site generation.

Save to ▾
Show answer
A single-page app loads once and renders views in the browser, giving app-like navigation but slower first load. A multi-page app renders each page on the server with full reloads. Server-side rendering generates HTML per request for fast content and SEO, then hydrates. Static site generation pre-builds HTML at build time and serves it from a CDN, which is fastest for content that changes rarely, like blogs.
02

What are higher-order functions and currying?

Save to ▾
Show answer
A higher-order function takes a function as an argument or returns one, like map, filter or a custom withLogging(fn) wrapper. Currying transforms a function with many arguments into a chain of single-argument functions, so add(2)(3) returns 5. Currying makes partial application easy, for example creating formatINR = formatCurrency('INR') once and reusing it. Overusing it can reduce readability for teams unfamiliar with functional style.
03

How does destructuring with default values work?

Save to ▾
Show answer
Destructuring pulls values out of arrays or objects into variables. Defaults apply only when the value is undefined, not when it is null. For example, const { name, city = 'Delhi' } = user sets city to Delhi if the property is missing. Function parameters can be destructured too, like function render({ title, size = 'md' } = {}), which avoids errors when no argument is passed and documents expected options clearly.
04

After a release, the app shows a blank page in some older browsers. What could be wrong?

Save to ▾
Show answer
The build probably targets newer syntax or uses an API that older browsers lack, causing a parse error that stops the whole bundle. I would check the console in that browser, compare the build target and browserslist with our support policy, and add missing polyfills for used APIs. Error monitoring with browser breakdowns catches this quickly, and testing on the oldest supported browser should be part of release checks.
05

What do caret and tilde mean in package versions, and why commit the lockfile?

Save to ▾
Show answer
Versions follow semantic versioning: major.minor.patch. ^1.4.2 allows any 1.x.x version at or above 1.4.2, and ~1.4.2 allows only patch updates within 1.4.x. Because ranges can resolve differently over time, the lockfile, such as package-lock.json or pnpm-lock.yaml, records exact installed versions. Committing it and using npm ci in CI ensures every developer and build uses identical dependencies.
06

What is the difference between local UI state, global state and server state?

Save to ▾
Show answer
Local state belongs to one component, like whether a dropdown is open. Global client state is shared across the app, like theme or the logged-in user. Server state is data owned by the backend, such as orders, that must be fetched, cached, refreshed and kept in sync. Treating server state separately with tools like TanStack Query removes much of the code teams once wrote in global stores.
07

Write a Vitest unit test for an INR price formatter.

Save to ▾
Show answer
Test normal, edge and invalid inputs: // format.ts export const formatINR = (paise: number) => new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(paise / 100); // format.test.ts import { describe, it, expect } from 'vitest'; import { formatINR } from './format'; describe('formatINR', () => { it('formats lakhs with Indian grouping', () => { expect(formatINR(12345600)).toBe('\u20b91,23,456.00'); }); it('handles zero', () => { expect(formatINR(0)).toBe('\u20b90.00'); }); });
08

How can you communicate between multiple tabs of the same site?

Save to ▾
Show answer
BroadcastChannel lets same-origin tabs, windows and workers publish and subscribe to messages on a named channel, which suits syncing logout, theme or cart updates. The storage event fires in other tabs when localStorage changes, which is a simple fallback. SharedWorker can hold shared state for all tabs. Each option works only within the same origin, so cross-domain sync needs a server or postMessage.
09

Create a type where all fields are optional except id.

Save to ▾
Show answer
Combine Pick and Partial with an intersection: interface Product { id: string; name: string; price: number; stock: number; } type ProductUpdate = Pick<Product, 'id'> & Partial<Omit<Product, 'id'>>; const update: ProductUpdate = { id: 'p1', price: 799 }; // const bad: ProductUpdate = { price: 799 }; // error: id missing This is a common pattern for PATCH request payloads, ensuring the identifier is always present.
10

How does the History API enable client-side routing?

Save to ▾
Show answer
history.pushState(state, '', '/courses/react') changes the URL and adds a history entry without reloading the page, and replaceState updates the current entry. When users press back or forward, the popstate event fires and the app renders the matching view. The server must return the app's HTML for all routes, otherwise refreshing a deep link gives a 404. Routers in SPA frameworks are built on this API.
11

Move a heavy calculation into a Web Worker.

Save to ▾
Show answer
The worker runs on its own thread and talks through messages: // worker.js self.onmessage = (e) => { const total = e.data.reduce((sum, row) => sum + row.amount, 0); self.postMessage(total); }; // main.js const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }); worker.postMessage(bigArrayOfRows); worker.onmessage = (e) => showTotal(e.data); The UI stays responsive. Data is copied between threads, so for huge binary data use transferable objects like ArrayBuffer.
12

How do browser permission APIs like Geolocation and Notifications work?

Save to ▾
Show answer
Powerful features require user consent, a secure HTTPS context and usually a user gesture. Calling navigator.geolocation.getCurrentPosition or Notification.requestPermission shows a browser prompt; if the user denies it, you cannot ask again easily. navigator.permissions.query lets you check the current state first. Good UX explains why you need the permission before triggering the prompt, for example asking for location only when the user taps 'Find nearest centre'.
13

Write a minimal observable store in plain JavaScript.

Save to ▾
Show answer
A store holds state and notifies subscribers: function createStore(initial) { let state = initial; const listeners = new Set(); return { get: () => state, set(update) { state = { ...state, ...update }; listeners.forEach(fn => fn(state)); }, subscribe(fn) { listeners.add(fn); return () => listeners.delete(fn); } }; } const cart = createStore({ items: 0 }); const stop = cart.subscribe(s => (badge.textContent = s.items)); cart.set({ items: 2 }); The returned unsubscribe function prevents memory leaks.
14

When should you use Map and Set instead of plain objects and arrays?

Save to ▾
Show answer
Map allows keys of any type, keeps insertion order, has a size property and performs well with frequent additions and deletions, so it suits caches and lookups by object. Set stores unique values with fast has() checks, ideal for tracking selected ids. Plain objects are better for fixed-shape records and JSON. Arrays are better when order and index access matter and duplicates are allowed.
15

Why does await inside forEach not wait for each item?

Save to ▾
Show answer
forEach ignores the promise returned by an async callback, so all callbacks start almost at once and forEach finishes before any await completes. Code after it runs too early. For sequential processing, use a for...of loop with await inside. For parallel processing with a final result, use await Promise.all(items.map(async item => ...)). Choosing between them depends on whether order and rate limits matter.
16

What are WeakMap and WeakRef, and when are they useful?

Save to ▾
Show answer
A WeakMap holds object keys weakly, so when no other reference to the key exists, the entry can be garbage collected. This makes it ideal for attaching private metadata or caching computed data for DOM nodes without causing memory leaks. WeakMap keys are not enumerable. WeakRef holds a weak reference to a single object and is rarely needed, mostly for caches, because garbage collection timing is unpredictable.
17

What are declaration files (.d.ts) and when do you write them?

Save to ▾
Show answer
Declaration files describe the types of JavaScript code without containing implementation. Libraries ship them so TypeScript users get type checking, and many come from DefinitelyTyped as @types packages. You write your own when using an untyped library, declaring global variables injected by scripts, or typing non-code imports like declare module '*.svg'. Generating them with declaration: true is standard when publishing a TypeScript library.
18

Compare Promise.all, Promise.allSettled, Promise.race and Promise.any.

Save to ▾
Show answer
Promise.all resolves with all results when every promise succeeds, but rejects immediately if any fails. Promise.allSettled always waits for all and returns each outcome with status fulfilled or rejected, useful for dashboards loading independent widgets. Promise.race settles with the first promise to settle, often used for timeouts. Promise.any resolves with the first success and rejects only if all fail, useful for trying multiple mirrors.
19

Product images on a listing page load slowly. What quick fixes would you apply?

Save to ▾
Show answer
I would convert images to WebP or AVIF, resize them to the actual thumbnail size instead of loading full product photos, and serve them through srcset. Images below the fold get loading='lazy', while the first few visible ones load eagerly. Adding width and height prevents layout shift, and serving images from a CDN with long cache headers makes repeat visits much faster.
20

Cancel an older search request when the user types again using AbortController.

Save to ▾
Show answer
Keep a controller per request and abort the previous one: let controller; async function search(term) { controller?.abort(); controller = new AbortController(); try { const res = await fetch('/api/search?q=' + encodeURIComponent(term), { signal: controller.signal }); renderResults(await res.json()); } catch (err) { if (err.name !== 'AbortError') showError(err); } } This prevents slow old responses from overwriting newer results.
21

What are Core Web Vitals and their good thresholds?

Save to ▾
Show answer
Core Web Vitals are Google's user experience metrics measured from real users. Largest Contentful Paint measures loading and should be 2.5 seconds or less. Interaction to Next Paint measures responsiveness and should be 200 milliseconds or less. Cumulative Layout Shift measures visual stability and should be 0.1 or less. A page passes when at least 75 percent of visits meet each threshold, and they influence search ranking.
22

Configure a path alias and an API proxy in Vite.

Save to ▾
Show answer
vite.config.ts: import { defineConfig } from 'vite'; import { fileURLToPath, URL } from 'node:url'; export default defineConfig({ resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }); Imports like '@/utils/format' work anywhere, and the proxy avoids CORS issues in development. Add matching paths in tsconfig for editor support.
23

What is transpilation and how does browserslist control it?

Save to ▾
Show answer
Transpilation converts modern JavaScript or TypeScript into syntax that target browsers understand. Tools include Babel, SWC and esbuild. A browserslist setting, such as '> 0.5%, last 2 versions, not dead', or a build target like es2020, decides which features need conversion and which polyfills to include. Targeting only modern browsers produces smaller, faster code, so the support list should be based on real analytics.
24

What is the difference between unit, integration and end-to-end tests?

Save to ▾
Show answer
A unit test checks a single function or module in isolation, such as a price formatter. An integration test checks several pieces working together, like a form component with validation and an API call mocked at the network level. An end-to-end test drives a real browser through a complete user flow, such as signup to dashboard, against a running app. They trade speed for realism in that order.
25

Does native CSS support nesting now?

Save to ▾
Show answer
Yes. All modern browsers support native CSS nesting, so you can write .card { padding: 1rem; & .title { font-weight: 600; } &:hover { box-shadow: ... } } without Sass. The ampersand refers to the parent selector. Deep nesting still creates high specificity and long selectors, so the usual advice from Sass days applies: keep nesting to one or two levels.
26

Which tsconfig options are important for a strict codebase?

Save to ▾
Show answer
strict: true enables a family of checks, including strictNullChecks and noImplicitAny, and should be on for new projects. noUncheckedIndexedAccess adds undefined to array and record lookups, catching many runtime errors. exactOptionalPropertyTypes, noImplicitOverride and noFallthroughCasesInSwitch add more safety. For bundler-based apps, moduleResolution: bundler, isolatedModules and verbatimModuleSyntax keep TypeScript aligned with how tools like Vite compile code.
27

How do private class fields and getters work in modern JavaScript?

Save to ▾
Show answer
Fields prefixed with #, such as #balance, are truly private and cannot be accessed outside the class body, unlike the old underscore convention. Getters and setters, written get total() and set total(v), let you expose computed or validated properties while keeping internal state hidden. Static fields and static blocks are also supported. These features make classes safer for building reusable UI components and service objects.
28

What is the difference between lab data and field data in performance?

Save to ▾
Show answer
Lab data comes from controlled tests like Lighthouse or WebPageTest on a fixed device and network, which is great for debugging and comparing changes. Field data, or real user monitoring, comes from actual visitors through the Chrome UX Report or your own analytics, capturing real devices, networks and behaviour. Google ranks on field data. A page can score well in the lab but fail in the field on low-end phones.
29

What is visual regression testing?

Save to ▾
Show answer
Visual regression testing captures screenshots of pages or components and compares them pixel by pixel with approved baselines, flagging unexpected visual changes such as broken spacing or wrong colours. Tools include Playwright's toHaveScreenshot, Chromatic with Storybook and Percy. To avoid false failures, run in consistent containers, disable animations, and mask dynamic content like dates. It catches CSS bugs that functional tests miss.
30

Implement a throttle function.

Save to ▾
Show answer
Throttle allows a function to run at most once in a given interval: function throttle(fn, wait) { let last = 0; return function (...args) { const now = Date.now(); if (now - last >= wait) { last = now; fn.apply(this, args); } }; } window.addEventListener('scroll', throttle(updateProgressBar, 100)); Unlike debounce, which waits for calls to stop, throttle fires regularly during continuous events such as scrolling or dragging.
31

Explain the utility types Partial, Pick, Omit and Record.

Save to ▾
Show answer
Partial<T> makes all properties optional, useful for update payloads. Pick<T, 'id' | 'name'> creates a type with only selected keys, and Omit<T, 'password'> removes keys, handy for public user objects. Record<K, V> builds an object type with keys K and values V, such as Record<'en' | 'hi', string> for translations. Others include Required, Readonly, ReturnType and Awaited.
32

How does Tailwind CSS keep the final CSS file small?

Save to ▾
Show answer
Tailwind scans your source files for class names and generates only the utilities you actually use, so the production CSS is usually a few kilobytes even though thousands of utilities are available. This requires class names to appear as complete strings in the code. Tailwind v4 uses a CSS-first configuration with @theme for design tokens and detects source files automatically.
33

Set up ESLint with the flat config for a TypeScript project.

Save to ▾
Show answer
Install packages and create eslint.config.js: // npm i -D eslint @eslint/js typescript-eslint import js from '@eslint/js'; import tseslint from 'typescript-eslint'; export default tseslint.config( js.configs.recommended, ...tseslint.configs.recommended, { rules: { '@typescript-eslint/no-explicit-any': 'warn', 'no-console': ['warn', { allow: ['error'] }] } }, { ignores: ['dist/'] } ); Run npx eslint . locally and in CI. Leave formatting to Prettier so the two tools do not conflict.
34

How do HTTP caching headers work for frontend assets?

Save to ▾
Show answer
Cache-Control controls caching. Files with content hashes in their names, like app.3f9a2c.js, can use max-age=31536000, immutable because a new build produces a new filename. HTML should use no-cache, which forces revalidation so users get new asset references after a deploy. ETag and Last-Modified allow cheap revalidation with 304 Not Modified responses. Wrong headers cause either stale apps or needless re-downloads.
35

Write a type-safe getProperty function using generics and keyof.

Save to ▾
Show answer
Constrain the key to the object's own keys: function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const course = { title: 'Frontend', weeks: 14 }; const weeks = getProperty(course, 'weeks'); // number // getProperty(course, 'price'); // compile error T[K] is an indexed access type, so the return type matches the chosen property. Typos in key names are caught at compile time.
36

What is TypeScript and why do teams adopt it?

Save to ▾
Show answer
TypeScript is a superset of JavaScript that adds static types and compiles to plain JavaScript. It catches errors such as wrong property names, missing arguments and null access while you type, before the code runs. It also improves editor autocomplete and makes refactoring large codebases safer. Types are erased at runtime, so TypeScript adds no performance cost but needs a build or type-stripping step.
37

Collect Core Web Vitals from real users with the web-vitals library.

Save to ▾
Show answer
Report metrics to your analytics endpoint: import { onLCP, onINP, onCLS } from 'web-vitals'; function send(metric) { const body = JSON.stringify({ name: metric.name, value: metric.value, rating: metric.rating, page: location.pathname }); navigator.sendBeacon('/analytics/vitals', body); } onLCP(send); onINP(send); onCLS(send); sendBeacon delivers data reliably even when the page is closing. Aggregate at the 75th percentile per page.
38

Create a new Vite project with TypeScript and run it.

Save to ▾
Show answer
Use the official scaffolding tool: npm create vite@latest my-app -- --template vanilla-ts cd my-app npm install npm run dev # starts the dev server with HMR npm run build # type-checks and builds to dist/ npm run preview # serves the production build locally Templates also exist for react-ts, vue-ts and svelte-ts. The dist folder contains static files ready for any web server or CDN.
39

Write a custom type guard to check if a value is a User.

Save to ▾
Show answer
A function returning value is User narrows the type: interface User { id: number; email: string; } function isUser(value: unknown): value is User { return typeof value === 'object' && value !== null && 'id' in value && typeof (value as User).id === 'number' && 'email' in value && typeof (value as User).email === 'string'; } const data: unknown = JSON.parse(raw); if (isUser(data)) console.log(data.email); Inside the if block, data is treated as User.
40

How do you choose the right image format and size?

Save to ▾
Show answer
Use AVIF or WebP for photos because they are much smaller than JPEG at similar quality, with JPEG as fallback. Use SVG for icons and logos, and PNG only when lossless transparency is essential. Serve sizes that match the display width with srcset and sizes, compress with tools like Squoosh or an image CDN, and always specify width and height to avoid layout shift.

Frequently asked questions

What happens in the Frontend Development online assessment round?

Most companies start with an online test: aptitude and reasoning, a few MCQs on core Frontend Development concepts, and one or two short hands-on tasks. It filters a large number of applicants, so speed and accuracy both count.

How do I clear the online assessment round?

Practise under a timer: about one minute per MCQ and 15-20 minutes per hands-on task. Read every option before answering; tests often include two nearly correct choices. Use a stable internet connection, keep your camera on and avoid switching tabs in proctored tests.

Which round comes next?

A typical order is online assessment, telephonic screening, one or two technical rounds, a managerial round and HR. Use the other round pages to practise each stage.