We use cookies for site analytics. Accept to help us understand how the site is used. See our Privacy Policy for details.
An easy frontend problem, graded against 6 test cases (4 of them hidden).
React and browser-platform problems - hooks, event handling, and rendering behaviour.
Reach for it when you see: A component, hook, or DOM-behaviour question rather than an algorithm.
More Frontendproblems →```tsx
type Props = {
initial?: number;
step?: number;
min?: number;
max?: number;
};
export function Counter({ initial = 0, step = 1, min = -Infinity, max = Infinity }: Props) {
const clamped = Math.min(max, Math.max(min, initial));
const [count, setCount] = useState(clamped);
const inc = () => setCount(c => Math.min(max, c + step));
const dec = () => setCount(c => Math.max(min, c - step));
const reset = () => setCount(clamped);
return (
<div>
<span aria-live="polite">{count}</span>
<button onClick={dec} disabled={count <= min}>-</button>
<button onClick={reset}>Reset</button>
<button onClick={inc} disabled={count >= max}>+</button>
</div>
);
}
```
Why paper-only here: the gitGood sandbox doesn't run React. The reducer is the testable core - extract it as a pure function (`counterReducer`) and the component becomes thin glue. This is also the right architecture for any non-trivial component: pure logic + React shell.
Interview talking points:
- Use the functional updater form (`setCount(c => ...)`) to avoid stale-closure bugs when handlers fire in quick succession.
- `aria-live="polite"` announces count changes to screen readers without interrupting.
- Disabling at boundaries (vs hiding) keeps button positions stable.
The full reference solution in every supported language stays in the editor above - reveal it there once you have had a real attempt.
These apply to the pattern as a whole, not just this problem.