Part 1: Building High Performance Virtualized Select in Material UI using react-window
6 min read
•
Jan 9, 2026
Hi there! Welcome to my blog, this is Alan.
Today is a good day, isn’t it? And I’m going to share with you how to build a Performant Select which can handle thousands of records in Material UI using react-window.
Press enter or click to view image in full size

Let’s get started!
- Initialize a project, use any framework you want. For me, I use NextJS which is a great react framework.
Press enter or click to view image in full size

Initialize project
2. Install Material UI (you can easily follow the document here) and react-window
yarn add react-window
yarn add --dev @types/react-window3. All set for the source code!
Now we go into the main part, the Performant Select
Create a directory component call PerformantSelect and index.tsx as the follow:
import Popover from "@mui/material/Popover";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { MouseEvent, useState } from "react";
export default function PerformantSelect() {
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const handleClick = (event: MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
return (
<div>
<Button aria-describedby={id} variant="contained" onClick={handleClick}>
Open Popover
</Button>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
>
<Typography sx={{ p: 2 }}>The content of the Popover.</Typography>
</Popover>
</div>
);
}Import the component into Home component:
Press enter or click to view image in full size

What we got here is a Button to open/close a pop-up content
Press enter or click to view image in full size

4. Let’s replace the content of the Popover with a List using react-window:
import Popover from "@mui/material/Popover";
import Button from "@mui/material/Button";
import { CSSProperties, MouseEvent, useState } from "react";
import { FixedSizeList } from "react-window";
import { Box, colors, Typography } from "@mui/material";
type Props = {
options: { value: string | number; label: string }[];
};
const Row = ({
index,
style,
data,
}: {
index: number;
style: CSSProperties;
data: Props["options"];
}) => {
const item = data[index];
return (
<Box
style={style}
sx={{
display: "flex",
alignItems: "center",
cursor: "pointer",
"&:hover": {
background: colors.blue[100],
},
}}
>
<Typography>{item.label}</Typography>
</Box>
);
};
export default function PerformantSelect({ options }: Props) {
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const handleClick = (event: MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
return (
<div>
<Button aria-describedby={id} variant="contained" onClick={handleClick}>
Open Popover
</Button>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
>
<Box sx={{ minWidth: 300, width: "100%" }}>
<FixedSizeList
itemData={options}
height={240}
itemCount={options.length}
itemSize={40}
width={"100%"}
itemKey={(index, _data) => _data[index].value}
>
{Row}
</FixedSizeList>
</Box>
</Popover>
</div>
);
}Create a list of 10,000 of users using fakerjs and pass it to the component
Press enter or click to view image in full size

Press enter or click to view image in full size

5. Let’s make the Button look like a Select UI
Replace the Button with Text Field and Input Label and add the style as below:
<FormControl onClick={handleClick} sx={{ marginLeft: 50, marginTop: 10 }}>
<InputLabel
sx={{ paddingLeft: 1, paddingRight: 1, background: "white" }}
shrink={open}
>
Select
</InputLabel>
<TextField
id="component-simple"
sx={{
"& .MuiInputBase-input": {
opacity: 0,
width: 0,
paddingRight: 0,
},
}}
slotProps={{
input: {
startAdornment: (
<Box
sx={{
width: 200,
cursor: "pointer",
lineHeight: "40px",
zIndex: 1,
display: "flex",
alignItems: "center",
alignSelf: "stretch",
}}
>
{/* Value */}
</Box>
),
endAdornment: (
<InputAdornment sx={{ cursor: "pointer" }} position="start">
<ExpandMoreIcon
sx={{ transform: open ? "rotate(180deg)" : "rotate(0)" }}
/>
</InputAdornment>
),
},
}}
/>
</FormControl>Here you go!
Press enter or click to view image in full size

Select UI
6. We got a better UI, but selecting an option does not work yet. Let’s make it work and display selected option.
Update the type
export type Option = { value: string | number; label: string };
type Props = {
options: Option[];
onChange: (item: Option) => void;
value?: Option;
};Define onChange function:
Press enter or click to view image in full size

We need to pass _onChange to the Row component and when user clicks on it, we invoke onChange to update selected option.
// Define what data will be passed into Row
const itemData = {
options,
onChange: _onChange,
};
// Update the FixedSizeList
<FixedSizeList
itemData={itemData}
height={240}
itemCount={options.length}
itemSize={40}
width={"100%"}
itemKey={(index, _data) => _data.options[index].value}
>
{Row}
</FixedSizeList>And also update the Row component:
Press enter or click to view image in full size

Display selected option

And we need to update the Label as well, make sure it will shrink if we have selected value, otherwise, it will overlap with the value

Update the Home component to pass value and onChange:
Press enter or click to view image in full size

Result:
Press enter or click to view image in full size

7. Our Select should support “multiple” as well, right?
Update the Props type:
type Props = {
options: Option[];
onChange: (item: Option | Option[]) => void;
value?: Option | Option[];
multiple?: boolean;
};The selected value(s) now can be an item or an array of items
Update the Display:
Press enter or click to view image in full size

Update _onChange function:
const _onChange = (item: Option) => {
if (multiple) {
let _value = Array.isArray(value) ? [...value] : [];
const index = _value.findIndex((i) => i.value === item.value);
if (index !== -1) {
_value.splice(index, 1);
} else {
_value.push(item);
}
onChange(_value);
} else {
onChange(item);
}
handleClose();
};At Home component:
// Update the value state
const [value, setValue] = useState<Option | Option[]>();
// enable multiple
<PerformantSelect
multiple
value={value}
options={options}
onChange={(item) => setValue(item)}
/>Result:
Press enter or click to view image in full size

It worked! But the UI doesn’t look great yet. How about adding checkboxes and background for selected items?
Define selectedValues and pass it to Row component:
Press enter or click to view image in full size

Update the Row component again:
Press enter or click to view image in full size

We just want to make sure the Checkbox is shown if multiple is enabled only.
And here we are!
Press enter or click to view image in full size

There is another piece to make this Select more professional that is the Search Box, but I think this post is very long til now. I will move that piece to Part 2.
With this Performant Select, you now can be confident to handle any size of select.
I’ll see you in the next part.
Thanks for reading!
Github: https://github.com/alanng2050/blog-material-ui-virtualized-select