Open mr-scrpt opened 2 weeks ago
The slownest is coming from rendering to much HTML that any browser can handle fast, when rendering a huge list you might want to consider using some Virtualization library, the way these library works is by rendering only the needed elements within the view it self and as you scrolling it deletes the items the isn't in the view and add new HTML elements which is within the current view.
I made an example where I wanted to select an item in ### 1000 elements list:
https://github.com/shadcn-ui/ui/assets/56927215/25258200-9fa5-4ba1-8b88-2a8cf3d26999
https://github.com/shadcn-ui/ui/assets/56927215/39955ef5-397c-4c96-b0b6-dd539487d3f0
Here is a code example:
First install react-window: npm i react-window
and if you are using typescript you have to also install npm i --save-dev @types/react-window
as well
Then in your Select component you need to hide scroll icon button:
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> & {
showScrollIcon?: boolean
}
>(
(
{
className,
children,
position = 'popper',
showScrollIcon = false,
...props
},
ref
) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
position === 'popper' &&
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
className
)}
position={position}
{...props}
>
{showScrollIcon && <SelectScrollUpButton />}
<SelectPrimitive.Viewport
className={cn(
'p-1',
position === 'popper' &&
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
)}
>
{children}
</SelectPrimitive.Viewport>
{showScrollIcon && <SelectScrollDownButton />}
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
)
Then in the SelectContent you need to wrap the items within "FixedSizeList" from "react-window":
<FixedSizeList
width={'100%'}
height={350}
itemCount={data.length}
itemSize={40}
>
{({ index, style, isScrolling }) => (
<SelectItem
value={data[index]}
key={data[index]}
style={{
...style,
}}
>
{data[index]}
</SelectItem>
)}
</FixedSizeList>
I've used random data generation to genrate a 1000 items for testing the code:
const data = Array.from({ length: 1000 }, (_, index) => `Item ${index}`)
'use client'
import { useState } from 'react'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { FixedSizeList } from 'react-window'
const data = Array.from({ length: 1000 }, (_, index) => `Item ${index}`)
export default function MyList() {
const [value, setValue] = useState('')
const onChange = (v: string) => {
setValue(v)
}
return (
<div className="p-16 space-y-8">
<div>
<h1 className="text-green-700">With Virtualization</h1>
<Select onValueChange={onChange} defaultValue={value} open={true}>
<SelectTrigger>
<SelectValue placeholder="Select a verified email to display" />
</SelectTrigger>
<SelectContent>
<FixedSizeList
width={'100%'}
height={350}
itemCount={data.length}
itemSize={40}
>
{({ index, style, isScrolling }) => (
<SelectItem
value={data[index]}
key={data[index]}
style={{
...style,
}}
>
{data[index]}
</SelectItem>
)}
</FixedSizeList>
</SelectContent>
</Select>
</div>
<div>
<h1 className="text-red-500">Without Virtualization</h1>
<Select onValueChange={onChange} defaultValue={value}>
<SelectTrigger>
<SelectValue placeholder="Select a verified email to display" />
</SelectTrigger>
<SelectContent showScrollIcon={false}>
{data.map((i) => (
<SelectItem value={i} key={i}>
{i}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
)
}
@OmarAljoundi Thank you, its works for me!!
Describe the bug
I use select to output a large number of items, there can be around 1 thousand and even sometimes more than that When I press select to get a dropdown list, on my device it takes about 3-4 seconds before the list of options appears on the screen. With native select everything works instantly
Affected component/components
Select
How to reproduce
Here is the code for my component
Here is what the postOfficeListToSelect array looks like
Codesandbox/StackBlitz link
No response
Logs
No response
System Info
Before submitting