Grid lights is a question that has come up in past Uber frontend interviews. Here's the question:
Build a 3x3 grid of squares (omitting the center square) where you can click on the squares to activate them, turning them green. When all the squares have been activated, they will be deactivated one by one in the reverse order they were activated with a 300ms interval in between.

Let's delve into how to solve this question and what clarifying questions to ask.
First, understand the requirements and ask clarifying questions
The first thing you want to do in any technical interview is ask clarifying questions that help you understand the requirements of the question being asked of you. After reaching the onsite stage at my top choice company, completing each technical exercise, but not receiving an offer (🥺), their main piece of feedback for me was to slow down and make sure I fully understand what I'm building before jumping into the code. I want to share that advice with you before you do the same!
I sometimes struggle to think of clarifying questions to ask, and my coworker shared a good piece of advice with me: Start by asking questions about requirements that already seem clear to you. This both gets you communicating with the interviewer and could show you that a requirement is different than you originally assumed.
So, let's take a minute and ask clarifying questions about the requirements of grid lights:
- What does it mean for a square to be deactivated? When a square is deactivated, it means it returns to its original, not-green state.
- Does omit the center square mean to not render anything? Yes
- After a square is clicked and activated, can you click it to deactivate it or should it be disabled? Let's assume that after a square is clicked and activated, you can't click it again.
- When the grid is deactivating, can you still click squares to re-activate them? Or, should each square in the grid be in a disabled state? When the grid is deactivating, you can't click squares to re-activate them.
- Should the squares in the grid be navigable by keyboard, or is clicking sufficient? Let's assume that just clicking is sufficient for now.
Second, come up with an outline of your solution
Now that we have a better understanding of the requirements, let's come up with a plan for the solution. We can divide this problem into three parts:
- Rendering the grid
- Handling clicks and controlling the click-order state
- Deactivation
Rendering the grid
There are different ways that you can render the grid using CSS. Since we're talking about a grid, the way that makes the most sense to me would be using CSS Grid
. First, we can render a grid div
container that will hold our 3x3 grid using CSS Grid
. Then, we can render a square div
or button
for each of the clickable squares in the grid. I'd use a button so we can set disabled states more easily.
Since we have the requirement of omitting the center square, we can set up a config object:
const config = [
[1, 1, 1],
[1, 0, 1],
[1, 1, 1],
];
We can map over this config object and render a square for each 1
in the array, and omit rendering anything for 0
. Flattening this array will give us a 1D array of squares that we can map over to render our grid.
Then, we can use CSS Grid
to lay out the cells in a 3x3 grid. We can use grid-template-columns
and grid-template-rows
to set the number of columns and rows, and we can use gap
to set the spacing between the squares. The center square becomes a blank <div />
.
Each clickable square is rendered as a button
element, which simplifies accessibility and allows us to easily manage the disabled state.
Handling clicks and controlling the click-order state
As far as handling clicks, we are going to want to set up an onclick
handler for each of the squares. We track which squares have been clicked using React's useState hook:
const [order, setOrder] = useState([]);
Each time a square is clicked:
- Check if the square is already in order. If so, do nothing.
- Otherwise, add the square's index to the order array.
- If the array length reaches 8 (i.e., all clickable squares are active), trigger the deactivation sequence.
To prevent the user from clicking squares while the deactivation process is happening, we can set a disabled
state for the square after it has been clicked.
Deactivation
Once all 8 squares have been activated, we can start the deactivation process, in the reverse order they were activated. Here is how we can do that:
- We start a timer using
setInterval
that will run every 300ms. - Inside that timer, we remove the last item from the
order
array using.pop()
, which will give us the last square that was activated. - We update the state with the new
order
array, which will cause the square to re-render and deactivate. - Once the
order
array is empty, we clear the interval to stop the deactivation process.
const deactivateGrid = () => {
const intervalId = setInterval(() => {
setOrder((originalOrder) => {
const deactivateOrder = [...originalOrder];
deactivateOrder.pop();
if (deactivateOrder.length === 0) {
clearInterval(intervalId);
}
return deactivateOrder;
});
}, 300);
};
Third, implement the solution
Once you have a plan, you can start implementing the solution. Throughout, test your logic incrementally. Get the grid rendering, then make one square clickable, then scale to all squares, then trigger the deactivation. Here's a simple implementation of the grid lights problem using React:
import { useState } from "react";
import "./App.css";
export default function App() {
const config = [
[1, 1, 1],
[1, 0, 1],
[1, 1, 1],
];
const [order, setOrder] = useState([]);
const deactivateGrid = () => {
const intervalId = setInterval(() => {
setOrder((originalOrder) => {
const deactivateOrder = [...originalOrder];
deactivateOrder.pop();
if (deactivateOrder.length === 0) {
clearInterval(intervalId);
}
return deactivateOrder;
});
}, 300);
};
return (
<div className="wrapper">
<div className="grid">
{config.flat(1).map((value, index) => {
return value === 1 ? (
<button
className={[
"square",
order.includes(index) && "square--activated",
].join(" ")}
onClick={()=> {
const newOrder= [...order, index];
setOrder(newOrder);
if (newOrder.length= config.flat(1).filter(Boolean).length) {
deactivateGrid();
}
}}
disabled={order.includes(index)}
aria-label={`square-${index}`}
/>
) : (
<div />
);
})}
</div>
</div>
);
}
And the CSS:
body {
font-family: sans-serif;
}
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.square {
height: 80px;
width: 80px;
border: 1px solid black;
margin: 8px;
background-color: transparent;
}
.square--activated {
background-color: green;
}
Conclusion
The grid lights problem is a great example of a frontend interview question that tests your understanding of state management, event handling, and rendering in React. By breaking down the problem into manageable parts, asking clarifying questions, and implementing the solution incrementally, you can effectively tackle this type of question in an interview setting.
Remember to talk out loud as you work through the problem, explaining your thought process to the interviewer. This not only helps them understand your approach but also gives you a chance to catch any mistakes or assumptions you might have made along the way.
Finally, practice is key. The more you practice solving problems like this, the more confident you'll become with the process, and the better you'll perform in interviews. Good luck!