CRED Frontend Interview Questions
Design-led. CSS, animation and interaction detail.
CRED is known for its design. It has open-sourced its NeoPOP design system, and its products lean on motion and fine interaction detail. Frontend candidates are usually expected to care about how things look and feel, and to know why an animation is smooth or not.
The member product is app-first, so check which platform a role is for. For web roles, candidates commonly describe a build task judged on finish as well as function, and rounds on JavaScript, React and rendering performance.
- Questions
- 15, 5 with free answers
- Interview rounds
- 4
- Last reviewed
- October 2026
CRED frontend interview process
The usual shape of the loop. A guide to what to prepare, not a promise of what you will get.
- 1
Assignment or machine coding
A take-home task or a live build. Attention to visual detail is part of the evaluation.
- 2
JavaScript and React fundamentals
Core language questions, hooks, and how React renders.
- 3
Frontend depth
The rendering pipeline, animation performance, component architecture and accessibility.
- 4
Culture and hiring manager
How you work, what you have built and why you made the choices you did.
What interviewers tend to look for
- CSS depth: stacking contexts, custom properties, pseudo-elements and layout without hacks.
- Animations that stay on the compositor, and knowing what causes jank.
- Component APIs that are pleasant to use.
- Finish: spacing, states and transitions that hold up when someone looks closely.
Your CRED mock interview
15 questions. Type an answer under each one and an AI reviewer scores it and tells you what you missed. Stuck on one? Ask for a hint. A free account gets 3 AI replies a day.
Your mock interview
0 of 15 answered
Answer a question and your score shows up here.
JavaScript
3 questions- Question 1 of 15JavaScript
CRED interviewer
Explain the event loop. In what order do a setTimeout, a promise callback and synchronous logs run?
Model answer
JavaScript runs on one call stack. When the stack is empty, the event loop first runs every queued microtask (promise callbacks,
queueMicrotask,MutationObserver), then takes one macrotask (a timer, an I/O callback, a UI event), and the browser may render in between.console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D'); // A, D, C, BSynchronous code finishes first (A, D). The promise callback is a microtask, so it runs before the timer, which is a macrotask, even with a 0 ms delay.
With
asyncfunctions, everything after anawaitis scheduled as a microtask. A common follow-up is a longer snippet mixing both; work through it queue by queue instead of guessing. - Question 2 of 15JavaScript
CRED interviewer
What is the difference between debounce and throttle? Implement debounce.
Model answer
Debounce waits until the calls stop for a set time, then runs once. Use it for a search box or for saving after the user stops typing. Throttle runs at most once per interval while the calls keep coming. Use it for scroll, resize and drag handlers.
function debounce(fn, wait) { let timer; return function (...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), wait); }; }Common follow-ups: keep
thisand the arguments (done above), add acancelmethod, add aleadingoption that fires on the first call, and then write throttle. - Question 3 of 15JavaScript
CRED interviewer
Implement pipe and compose.
Model answer
Both combine functions into one.
piperuns them left to right,composeright to left.const pipe = (...fns) => (input) => fns.reduce((value, fn) => fn(value), input); const compose = (...fns) => (input) => fns.reduceRight((value, fn) => fn(value), input); const slugify = pipe( (text) => text.trim(), (text) => text.toLowerCase(), (text) => text.split(' ').join('-') ); slugify(' Hello World '); // "hello-world"Follow-ups: let the first function take several arguments, and write an async version that awaits each step (
reduceover a promise chain).
TypeScript
1 question- Question 4 of 15TypeScript
CRED interviewer
When would you use an enum, a union of string literals, or
as const?Model answer
A union of string literals (
'admin' | 'editor') exists only at compile time. It adds nothing to the bundle and autocompletes well. It is the usual choice.An
enumproduces a real object at runtime. Numeric enums also get a reverse mapping and accept any number, which is a known weak spot. Because enums are not plain JavaScript, some toolchains restrict them.When you need the values at runtime as well, for example to render a dropdown, write them once with
as constand derive the type:const ROLES = ['admin', 'editor', 'viewer'] as const; type Role = (typeof ROLES)[number]; // 'admin' | 'editor' | 'viewer' const LABELS = { admin: 'Administrator', editor: 'Editor', viewer: 'Read only', } satisfies Record<Role, string>;as constmakes the value deeply read-only and keeps the literal types.satisfieschecks a value against a type without widening it, soLABELS.adminis still known to be that exact string.
React
4 questions- Question 5 of 15React
CRED interviewer
What makes a component re-render, and how do you cut unnecessary re-renders?
Model answer
A component re-renders when its own state changes, when its parent re-renders, or when a context it reads changes. A changed prop is not a separate cause: props change because the parent rendered.
A re-render is React calling the function again. It is not a DOM update, and most are cheap. Measure with the React DevTools Profiler before changing anything.
When one is a real problem, in rough order of preference:
- Move state down into the component that uses it, so fewer components sit below it.
- Pass expensive content as
children. Elements created by a parent that did not re-render are reused as they are. - Wrap a costly child in
React.memoand give it stable props. - Split a large context, or use a store that lets components subscribe to a slice.
- Virtualise long lists.
- Question 6 of 15React
CRED interviewer
What is useRef for? How do you pass a ref to a child component?
Model answer
useRefreturns an object with acurrentproperty that stays the same between renders. Changingcurrentdoes not cause a re-render. Two uses:- Reaching a DOM node, to focus it, measure it or scroll to it.
- Keeping a value that should survive renders but is not shown: a timer id, the previous value of a prop, the latest version of a callback.
Do not read or write
currentwhile rendering. Use it in effects and event handlers.function TextField({ ref, ...props }) { return <input ref={ref} {...props} />; } function Form() { const inputRef = useRef(null); return ( <> <TextField ref={inputRef} /> <button onClick={() => inputRef.current?.focus()}>Edit</button> </> ); }In React 19,
refis an ordinary prop on function components, as above. In React 18 and earlier the child has to be wrapped inforwardRef.useImperativeHandlelets a component expose a small API, such asopen()andclose(), instead of its DOM node. - Question 7 of 15React
CRED interviewer
What are useTransition, useDeferredValue and Suspense for?
Model answer
Since React 18, rendering can be interrupted. These APIs let you tell React which updates are urgent.
const [query, setQuery] = useState(''); const [filter, setFilter] = useState(''); const [isPending, startTransition] = useTransition(); function onChange(event) { setQuery(event.target.value); // urgent: the input must keep up startTransition(() => setFilter(event.target.value)); // can wait }useTransitionmarks an update as non-urgent. If the user types again, React drops the half-finished render and starts over.isPendinglets you show that work is in progress.useDeferredValuedoes the same for a value you receive and do not control: it gives you a copy that lags behind during heavy renders.Suspenseshows a fallback while something below it is not ready: lazily loaded code, or data read through a framework or theusehook.
None of this makes rendering faster. It keeps slow rendering from blocking typing and clicks. Unlike a debounce there is no fixed delay, so a fast device shows the result immediately.
- Question 8 of 15React
CRED interviewer
Which component composition patterns do you use? How do you avoid prop drilling?
Model answer
Composition means building a component out of the pieces it is given instead of configuring it with a growing list of flags.
childrenand slot props:<Card header={<Title />}>...</Card>beats<Card title showIcon iconPosition>.- Compound components:
<Tabs>,<Tabs.List>and<Tabs.Panel>share state through a private context, and the caller arranges them freely. - Custom hooks for sharing logic. They have replaced most higher-order components and render props.
- Controlled and uncontrolled modes: accept
valueandonChange, and fall back to internal state when they are absent.
Composition also fixes most prop drilling. If
PagepassesuserthroughLayoutandHeaderonly so thatAvatarcan read it, havePagerender<Avatar user={user} />and pass that element down. The components in between no longer know aboutuser.Use Context for what is left.
CSS
5 questions- Question 9 of 15CSS
CRED interviewer
What is the difference between reflow and repaint? How do you keep animations smooth?
Model answer
The browser renders in steps: style, layout, paint, composite.
- Changing geometry (
width,top,margin, font size, adding nodes) forces layout, also called reflow, for the affected part of the tree, and then a repaint. - Changing appearance only (
color,background,box-shadow) skips layout but still repaints. - Changing
transformoropacitycan be done by the compositor alone, without layout or paint. These are the properties to animate.
/* Janky: recalculates layout every frame */ .panel { transition: left 200ms; } /* Smooth: handled by the compositor */ .panel { transition: transform 200ms; } .panel.open { transform: translateX(240px); }Layout thrashing is the other common cause of jank. Reading a layout value such as
offsetHeightorgetBoundingClientRect()straight after writing a style forces the browser to run layout immediately. In a loop that is one forced layout per iteration. Do all the reads first, then all the writes, or schedule writes inrequestAnimationFrame.will-changehints that an element will animate. Use it on a few elements, since each one takes memory. - Changing geometry (
- Question 10 of 15CSS
CRED interviewer
Explain the position values. Why does z-index sometimes not work?
Model answer
static: the default. Offsets andz-indexhave no effect.relative: stays in the flow and can be nudged. It also becomes the reference for absolutely positioned children.absolute: removed from the flow and placed relative to the nearest positioned ancestor.fixed: placed relative to the viewport, unless an ancestor has atransformorfilter, which captures it.sticky: behaves as relative until it reaches the offset you give it (top: 0), then sticks inside its scrolling ancestor. It fails without an offset, or when an ancestor hasoverflow: hidden.
z-indexonly orders elements inside the same stacking context. A new context is created by a positioned element with az-index, byopacitybelow 1, and bytransform,filterandisolation: isolate, among others.So a modal with
z-index: 9999can still sit under a header withz-index: 2, if the modal is inside a parent that forms its own context and that parent is below the header. The fix is to move the element out, usually with a portal, or to change the parent's stacking order. - Question 11 of 15CSS
CRED interviewer
What are CSS custom properties, and how would you build a dark theme with them?
Model answer
A custom property is a value you name with two dashes and read with
var(). Unlike a Sass variable, it exists at runtime: it cascades, it is inherited, and it can be changed by a class, a media query or JavaScript without rebuilding anything.:root { --bg: #ffffff; --text: #0f172a; --accent: #4f46e5; } [data-theme='dark'] { --bg: #0b1020; --text: #e2e8f0; --accent: #818cf8; } body { background: var(--bg); color: var(--text); }Components only use the variables, so switching the theme means changing one attribute on the root element.
- Default to the system setting with
@media (prefers-color-scheme: dark)and let the user override it. - Save the choice, and apply it with a small inline script in the head before the page paints. Otherwise the wrong theme flashes on load.
- Set
color-scheme: light darkso that form controls and scrollbars match. var(--gap, 8px)takes a fallback, andelement.style.setProperty('--x', value)sets one from JavaScript.
- Default to the system setting with
- Question 12 of 15CSS
CRED interviewer
What is the difference between a pseudo-class and a pseudo-element?
Model answer
A pseudo-class, written with one colon, selects an element in a particular state or position:
:hover,:focus-visible,:disabled,:checked,:first-child,:nth-child(2n),:not(.active).A pseudo-element, written with two colons, styles a part of an element or a box that is not in the HTML:
::before,::after,::placeholder,::selection,::marker,::first-line./* Pseudo-class: a state */ .field:focus-visible { outline: 2px solid var(--accent); } /* Pseudo-element: a generated box. It needs content. */ .required::after { content: ' *'; color: crimson; } /* :has() selects a parent by what it contains */ .card:has(img) { padding-top: 0; }Two that are worth knowing well:
:focus-visibleshows a focus ring for keyboard users without showing it on every mouse click, and:has()lets a parent or an earlier sibling be styled by what follows it, which used to need JavaScript. - Question 13 of 15CSS
CRED interviewer
Compare CSS Modules, utility classes such as Tailwind, and CSS-in-JS.
Model answer
- CSS Modules: ordinary CSS files whose class names are made unique at build time. No runtime cost and no naming collisions. You still write and name every class.
- Utility classes (Tailwind): you compose small single-purpose classes in the markup. Values come from a fixed scale, which keeps spacing and colour consistent, and the CSS file stops growing because classes are reused. The markup gets long and there is a vocabulary to learn.
- Runtime CSS-in-JS (styled-components, Emotion): styles are written in JavaScript and can depend on props. Styles sit next to the component. The cost is work in the browser on every render to generate and inject them, and they do not fit React Server Components, which cannot run that code.
- Build-time CSS-in-JS (vanilla-extract and similar): styles are written in TypeScript and extracted to static CSS files, which keeps type safety without the runtime cost.
There is no single right answer. The interviewer wants the trade-offs: runtime cost, how well it works with server rendering, how it scales to a large team, and how design tokens are enforced. Say what you have used and what went wrong with it.
Machine coding
2 questions- Question 14 of 15Machine coding
CRED interviewer
Build an image carousel.
Model answer
State is one number, the current index. Next and previous wrap around with modulo arithmetic.
const next = () => setIndex((i) => (i + 1) % slides.length); const prev = () => setIndex((i) => (i - 1 + slides.length) % slides.length); <div className="viewport"> <div className="track" style={{ transform: 'translateX(-' + index * 100 + '%)' }} > {slides.map((slide) => ( <img key={slide.src} src={slide.src} alt={slide.alt} /> ))} </div> </div>The track is a flex row inside a viewport with
overflow: hidden, moved with atransformso the slide animates on the compositor.- Previous and next buttons with accessible labels, and dot indicators that jump to a slide.
- Autoplay with an interval that is cleared on unmount and paused on hover and on focus.
- Left and right arrow keys.
- Lazy-load images other than the current one and its neighbours.
- Handle zero and one slide without errors.
Follow-ups: swipe support with pointer events, an endless loop without a visible jump from last to first, and
aria-roledescription="carousel"with each slide labelled "2 of 5". - Question 15 of 15Machine coding
CRED interviewer
Build an accessible modal dialog.
Model answer
- Render it through a portal into
document.body, so that a parent'soverflowor stacking context cannot clip it. - The parent owns the
openstate and passesonClose. - Close on the Escape key, on a click on the backdrop and on the close button. A click inside the dialog must not close it.
- On open, move focus into the dialog. While it is open, Tab and Shift+Tab must cycle inside it. On close, return focus to the element that opened it.
- Stop the page behind from scrolling while it is open.
- Mark it up with
role="dialog",aria-modal="true"andaria-labelledbypointing at its title.
function Modal({ open, onClose, title, children }) { const ref = useRef(null); useEffect(() => { const dialog = ref.current; if (open && !dialog.open) dialog.showModal(); if (!open && dialog.open) dialog.close(); }, [open]); return ( <dialog ref={ref} onClose={onClose} aria-labelledby="modal-title"> <h2 id="modal-title">{title}</h2> {children} <button onClick={onClose}>Close</button> </dialog> ); }The native
dialogelement withshowModal()gives you the focus trap, Escape handling, the backdrop and the top layer for free. Mention it even if the interviewer wants the manual version, and be ready to write the focus trap by hand. - Render it through a portal into
Practise for CRED
Reading answers is the easy half. These problems run in the browser against real tests.
