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
1yarn add react-window2yarn add --dev @types/react-window
3. 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:
1import Popover from "@mui/material/Popover";2import Typography from "@mui/material/Typography";3import Button from "@mui/material/Button";4import { MouseEvent, useState } from "react";56export default function PerformantSelect() {7 const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);89 const handleClick = (event: MouseEvent<HTMLElement>) => {10 setAnchorEl(event.currentTarget);11 };1213 const handleClose = () => {14 setAnchorEl(null);15 };1617 const open = Boolean(anchorEl);18 const id = open ? "simple-popover" : undefined;1920 return (21 <div>22 <Button aria-describedby={id} variant="contained" onClick={handleClick}>23 Open Popover24 </Button>25 <Popover26 id={id}27 open={open}28 anchorEl={anchorEl}29 onClose={handleClose}30 anchorOrigin={{31 vertical: "bottom",32 horizontal: "left",33 }}34 >35 <Typography sx={{ p: 2 }}>The content of the Popover.</Typography>36 </Popover>37 </div>38 );39}
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:
1import Popover from "@mui/material/Popover";2import Button from "@mui/material/Button";3import { CSSProperties, MouseEvent, useState } from "react";4import { FixedSizeList } from "react-window";5import { Box, colors, Typography } from "@mui/material";67type Props = {8 options: { value: string | number; label: string }[];9};1011const Row = ({12 index,13 style,14 data,15}: {16 index: number;17 style: CSSProperties;18 data: Props["options"];19}) => {20 const item = data[index];2122 return (23 <Box24 style={style}25 sx={{26 display: "flex",27 alignItems: "center",28 cursor: "pointer",29 "&:hover": {30 background: colors.blue[100],31 },32 }}33 >34 <Typography>{item.label}</Typography>35 </Box>36 );37};3839export default function PerformantSelect({ options }: Props) {40 const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);4142 const handleClick = (event: MouseEvent<HTMLElement>) => {43 setAnchorEl(event.currentTarget);44 };4546 const handleClose = () => {47 setAnchorEl(null);48 };4950 const open = Boolean(anchorEl);51 const id = open ? "simple-popover" : undefined;5253 return (54 <div>55 <Button aria-describedby={id} variant="contained" onClick={handleClick}>56 Open Popover57 </Button>58 <Popover59 id={id}60 open={open}61 anchorEl={anchorEl}62 onClose={handleClose}63 anchorOrigin={{64 vertical: "bottom",65 horizontal: "left",66 }}67 >68 <Box sx={{ minWidth: 300, width: "100%" }}>69 <FixedSizeList70 itemData={options}71 height={240}72 itemCount={options.length}73 itemSize={40}74 width={"100%"}75 itemKey={(index, _data) => _data[index].value}76 >77 {Row}78 </FixedSizeList>79 </Box>80 </Popover>81 </div>82 );83}
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:
1<FormControl onClick={handleClick} sx={{ marginLeft: 50, marginTop: 10 }}>2 <InputLabel3 sx={{ paddingLeft: 1, paddingRight: 1, background: "white" }}4 shrink={open}5 >6 Select7 </InputLabel>8 <TextField9 id="component-simple"10 sx={{11 "& .MuiInputBase-input": {12 opacity: 0,13 width: 0,14 paddingRight: 0,15 },16 }}17 slotProps={{18 input: {19 startAdornment: (20 <Box21 sx={{22 width: 200,23 cursor: "pointer",24 lineHeight: "40px",25 zIndex: 1,26 display: "flex",27 alignItems: "center",28 alignSelf: "stretch",29 }}30 >31 {/* Value */}32 </Box>33 ),34 endAdornment: (35 <InputAdornment sx={{ cursor: "pointer" }} position="start">36 <ExpandMoreIcon37 sx={{ transform: open ? "rotate(180deg)" : "rotate(0)" }}38 />39 </InputAdornment>40 ),41 },42 }}43 />44</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
1export type Option = { value: string | number; label: string };2type Props = {3 options: Option[];4 onChange: (item: Option) => void;5 value?: Option;6};
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.
1// Define what data will be passed into Row2const itemData = {3 options,4 onChange: _onChange,5};67// Update the FixedSizeList8<FixedSizeList9 itemData={itemData}10 height={240}11 itemCount={options.length}12 itemSize={40}13 width={"100%"}14 itemKey={(index, _data) => _data.options[index].value}15>16 {Row}17</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:
1type Props = {2 options: Option[];3 onChange: (item: Option | Option[]) => void;4 value?: Option | Option[];5 multiple?: boolean;6};
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:
1const _onChange = (item: Option) => {2 if (multiple) {3 let _value = Array.isArray(value) ? [...value] : [];4 const index = _value.findIndex((i) => i.value === item.value);5 if (index !== -1) {6 _value.splice(index, 1);7 } else {8 _value.push(item);9 }10 onChange(_value);11 } else {12 onChange(item);13 }1415 handleClose();16};
At Home component:
12// Update the value state3const [value, setValue] = useState<Option | Option[]>();45// enable multiple6 <PerformantSelect7 multiple8 value={value}9 options={options}10 onChange={(item) => setValue(item)}11 />
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