Transfer List
Difficulty: Advanced
Problem Statementβ
Two lists with buttons to move selected items left/right using checkbox selection.
Live Demoβ
Try the running app below β this is the target behavior for the challenge.
Transfer List β Live Demo
List A
- HTML
- CSS
- JavaScript
List B
- TypeScript
What Interviewers Expectβ
- Independent list state.
- Selection tracked via Set/checkbox.
- Disable transfer buttons when nothing selected.
Step-by-Step Implementationβ
Step 1 β Two list statesβ
Maintain left and right arrays separately.
const [left, setLeft] = useState(['HTML', 'CSS']);
const [right, setRight] = useState(['TypeScript']);
Step 2 β Selection Setβ
Track selected item names per list.
const [selLeft, setSelLeft] = useState(new Set());
Step 3 β Move selected itemsβ
Filter moved items out of source and append to destination.
const moving = from.filter((x) => selected.has(x));
Complete Implementationβ
Copy-paste ready single-file solution. Use this as your reference after attempting the challenge yourself, or paste it into CodeSandbox / StackBlitz to run locally.
App.jsx
import React, {useState} from 'react';
function TransferPanel({items, selected, setSelected, label}) {
const toggle = (name) => {
setSelected((prev) => {
const next = new Set(prev);
if (next.has(name)) next.delete(name);
else next.add(name);
return next;
});
};
return (
<div
style={{
flex: 1,
minWidth: 220,
border: '1px solid #ccc',
borderRadius: 8,
padding: '0.75rem',
}}
>
<strong>{label}</strong>
<ul style={{listStyle: 'none', padding: 0, marginTop: '0.5rem'}}>
{items.map((name) => (
<li key={name} style={{display: 'flex', gap: '0.5rem', marginBottom: '0.35rem'}}>
<input
type="checkbox"
checked={selected.has(name)}
onChange={() => toggle(name)}
/>
{name}
</li>
))}
</ul>
</div>
);
}
export default function App() {
const [left, setLeft] = useState(['HTML', 'CSS', 'JavaScript']);
const [right, setRight] = useState(['TypeScript']);
const [selLeft, setSelLeft] = useState(new Set());
const [selRight, setSelRight] = useState(new Set());
const move = (from, setFrom, to, setTo, selected, clearSelected) => {
const moving = from.filter((item) => selected.has(item));
if (!moving.length) return;
setFrom(from.filter((item) => !selected.has(item)));
setTo([...to, ...moving]);
clearSelected(new Set());
};
return (
<div style={{display: 'flex', gap: '0.75rem', alignItems: 'flex-start'}}>
<TransferPanel
items={left}
selected={selLeft}
setSelected={setSelLeft}
label="List A"
/>
<div style={{display: 'grid', gap: '0.5rem', alignSelf: 'center'}}>
<button
type="button"
disabled={!selLeft.size}
onClick={() => move(left, setLeft, right, setRight, selLeft, setSelLeft)}
>
β
</button>
<button
type="button"
disabled={!selRight.size}
onClick={() => move(right, setRight, left, setLeft, selRight, setSelRight)}
>
β
</button>
</div>
<TransferPanel
items={right}
selected={selRight}
setSelected={setSelRight}
label="List B"
/>
</div>
);
}
Final Checklistβ
- UI works for primary flow
- Edge cases handled (empty/disabled/loading where relevant)
- State updates are immutable
- Components are readable and reasonably split
- You can explain trade-offs out loud in an interview
Next Challengeβ
Continue to the next item in the sidebar when you're comfortable implementing this from scratch in 30β45 minutes.