RT
Rehan Tufail

Product Listing Page - Copy this React, Tailwind Component to your project

Import React, { useState } from 'react' import { AiFillStar } from "react icons/ai"; import { LayoutGrid, Menu, Heart, ChevronRight, ChevronLeft } from "lucide react" import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" import { Separator } from "@/components/ui/separator" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" export default function FilterTest() { const data = [ { id: "1", title: "Canon Cmera EOS 2000, Black 10x zoom", img: "/mobile.png", newPrice: "998", oldPrice: "1128", rating: "7.5", ordered: "154", shipping: "Free", description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" }, { id: "2", title: "GoPro HERO6 4K Action Camera Black", img: "/mobile2.png", newPrice: "998", oldPrice: "1128", rating: "7.5", ordered: "154", shipping: "Free", description: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit " }, { id: "3", title: "GoPro HERO6 4K Action Camera Black", img: "/tablet.png", newPrice: "998", oldPrice: "1128", rating: "7.5", ordered: "154", shipping: "Free", description: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit " }, { id: "4", title: "GoPro HERO6 4K Action Camera Black", img: "/laptop.png", newPrice: "998", oldPrice: "1128", rating: "7.5", ordered: "154", shipping: "Free", description: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit " }, { id: "5", title: "GoPro HERO6 4K Action Camera Black", img: "/watch.png", newPrice: "998", oldPrice: "1128", rating: "7.5", ordered: "154", shipping: "Free", description: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit " }, { id: "6", title: "GoPro HERO6 4K Action Camera Black", img: "/headphone.png", newPrice: "998", oldPrice: "1128", rating: "7.5", ordered: "154", shipping: "Free", description: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit " }, ]; const [minRating, setMinRating] = useState(0); const [minValue, setMinValue] = useState(0); // Input for Minimum Value const [maxValue, setMaxValue] = useState(100); // Input for Maximum Value const [range, setRange] = useState({ min: 0, max: 100 }); // Range slider values const [sliderValues, setSliderValues] = useState([0, 100]); // Values for the slider tips const handleSubmit = () => { if (minValue < maxValue) { setRange({ min: minValue, max: maxValue }); setSliderValues([minValue, maxValue]); } else { alert("Minimum value must be less than the maximum value!"); } }; const handleSliderChange = (e) => { const newValue = Number(e.target.value); if (e.target.name === "min") { setMinValue(newValue); if (newValue < maxValue) { setSliderValues([newValue, sliderValues[1]]); } } else { setMaxValue(newValue); if (newValue > minValue) { setSliderValues([sliderValues[0], newValue]); } } }; return ( <div className='w [85vw] m auto'> {/* Breadcrumbs */} <Breadcrumb> <BreadcrumbList className="text gray 400 py 4"> <BreadcrumbItem> <BreadcrumbLink href="/">Home</BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator /> <BreadcrumbItem> <BreadcrumbLink href="/">Mobile</BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator /> <BreadcrumbItem> <BreadcrumbPage className="text gray 400">Accessories</BreadcrumbPage> </BreadcrumbItem> </BreadcrumbList> </Breadcrumb> {/* main rendering div */} <div className='flex gap 6'> {/* Filters side div */} <div className='h full w 2/12'> <Separator /> <Accordion className="" type="single" collapsible defaultValue="item 1"> <AccordionItem value="item 1"> <AccordionTrigger className='text md font md hover:no underline transition ease in out duration 700 my 2'>Categories</AccordionTrigger> {[ "Mobile accessories", "Electronics", "Smartphones", "Modern tech", "See all", ].map((item, index) => ( <AccordionContent className={`py 2 text md ${item === "See all" ? "text blue 500" : "text gray 600" } duration 700`} key={index}> {item} </AccordionContent> ))} </AccordionItem> </Accordion> {/* Brand filter */} <Separator /> <Accordion className="" type="single" collapsible defaultValue="item 1"> <AccordionItem value="item 1"> <AccordionTrigger className="text md font md hover:no underline transition ease in out duration 700 my 2"> Brands </AccordionTrigger> {[ "Samsung", "Apple", "Huawei", "Pocco", "Lenovo", "See all", ].map((item, index) => ( <AccordionContent className={`py 2 text md flex items center gap 2 ${item === "See all" ? "text blue 500" : "text gray 600" } duration 700`} key={index} > {item !== "See all" && ( <Input type="checkbox" className="w 4 h 4 accent blue 500" /> )} {item} </AccordionContent> ))} </AccordionItem> </Accordion> {/* Features filter */} <Separator /> <Accordion className="" type="single" collapsible defaultValue="item 1"> <AccordionItem value="item 1"> <AccordionTrigger className="text md font md hover:no underline transition ease in out duration 700 my 2"> Features </AccordionTrigger> {[ "Metalic", "Plastic cover", "8GB Ram", "Super power", "Large memory", "See all", ].map((item, index) => ( <AccordionContent className={`py 2 text md flex items center gap 2 ${item === "See all" ? "text blue 500" : "text gray 600" } duration 700`} key={index} > {item !== "See all" && ( <Input type="checkbox" className="w 4 h 4 accent blue 500" /> )} {item} </AccordionContent> ))} </AccordionItem> </Accordion> {/* price range filter */} <Separator /> <Accordion className="" type="single" collapsible defaultValue="item 1"> <AccordionItem value="item 1"> <AccordionTrigger className="text md font md hover:no underline transition ease in out duration 700 my 2"> Price range </AccordionTrigger> <AccordionContent className='text md flex items center'> <div className=""> {/* Range Slider */} <div className="relative w full mb 6"> <div className="flex justify between text sm text gray 600 mb 2"> <span>{sliderValues[0]}</span> <span>{sliderValues[1]}</span> </div> <div className="relative h 1 bg blue 100 rounded full"> <div className="absolute h 1 bg blue 500 rounded full" style={{ left: `${((sliderValues[0] range.min) / (range.max range.min)) * 100}%`, right: `${100 ((sliderValues[1] range.min) / (range.max range.min)) * 100}%`, }} ></div> </div> <input type="range" name="min" min={range.min} max={range.max} value={sliderValues[0]} onChange={handleSliderChange} className="absolute appearance none w full h 0 bg transparent pointer events auto z 10 [&:: webkit slider thumb]:appearance none [&:: webkit slider thumb]:w 4 [&:: webkit slider thumb]:h 4 [&:: webkit slider thumb]:bg white [&:: webkit slider thumb]:rounded full [&:: webkit slider thumb]:border [&:: webkit slider thumb]:border blue 500 [&:: webkit slider thumb]:shadow md" /> <input type="range" name="max" min={range.min} max={range.max} value={sliderValues[1]} onChange={handleSliderChange} className="absolute appearance none w full h 0 bg transparent pointer events auto z 10 [&:: webkit slider thumb]:appearance none [&:: webkit slider thumb]:w 4 [&:: webkit slider thumb]:h 4 [&:: webkit slider thumb]:bg white [&:: webkit slider thumb]:rounded full [&:: webkit slider thumb]:border [&:: webkit slider thumb]:border blue 500 [&:: webkit slider thumb]:shadow md" /> </div> {/* Input Fields */} <div className="flex gap 4"> <div className="flex flex col items start justify between"> <label className="text sm text gray 600">Min</label> <input type="number" value={minValue} onChange={(e) => setMinValue(Number(e.target.value))} className="w 24 p 2 border border gray 200 bg white rounded md focus:outline none focus:ring focus:ring blue 200" /> </div> <div className="flex flex col items start justify between"> <label className="text sm text gray 600">Max</label> <input type="number" value={maxValue} onChange={(e) => setMaxValue(Number(e.target.value))} className="w 24 p 2 border border gray 200 bg white rounded md focus:outline none focus:ring " /> </div> </div> {/* Submit Button */} <Button onClick={handleSubmit} className="mt 3 w full bg white text blue 500 hover:text white shadow none border border gray 200 hover:bg blue 600 transition" > Apply </Button> </div> </AccordionContent> </AccordionItem> </Accordion> {/* Condition filter */} <Separator /> <Accordion className="" type="single" collapsible defaultValue="item 1"> <AccordionItem value="item 1"> <AccordionTrigger className="text md font md hover:no underline transition ease in out duration 700 my 2"> Condition </AccordionTrigger> {[ "Any", "Refurbished", "Brand new", "Old items", ].map((item, index) => ( <AccordionContent className='py 2 text md flex items center gap 2 duration 700' key={index} value={item} > <Input type="radio" className="w 4 h 4 accent blue 500" /> {item} </AccordionContent> ))} </AccordionItem> </Accordion> {/* Ratings filter */} <Separator /> <Accordion type="single" collapsible defaultValue="item 1"> <AccordionItem value="item 1"> <AccordionTrigger className="text md font semibold hover:no underline transition ease in out duration 700 my 2"> Ratings </AccordionTrigger> <AccordionContent> <div className="flex flex col space y 2 mt 2"> {[5, 4, 3, 2, 1].map((rating) => ( <button key={rating} onClick={() => setMinRating(rating)} className={`flex items center py 2 w full rounded lg`} > {/* Rating Stars */} <div className="flex items center"> <Input type="checkbox" className="mr 2 w 4 h 4 accent blue 400" /> {[...Array(5)].map((_, i) => ( <AiFillStar key={i} size={22} className={i < rating ? "text yellow 500" : "text gray 300"} /> ))} </div> </button> ))} </div> </AccordionContent> </AccordionItem> </Accordion> </div> {/* Cards and items main rendering */} <div className='w 9/12 h full flex flex col gap 6'> {/* Listing header with layout options */} <div className='bg white w full h 14 flex items center justify between border border gray 200 px 4 rounded sm'> <div className='flex gap 1 items center'> <p className='text md'>12,911 items in</p> <p className='text md font semibold'>Mobile accessory</p> </div> <div className='flex items center gap 4'> <span className='flex items center gap 1'> <Input type="checkbox" className="w 4 h 4 accent blue 500" /> <p className='text sm'>Verified only</p> </span> <Select > <SelectTrigger className="w [130px] rounded sm border border gray 200 focus:ring 0 shadow none"> <SelectValue placeholder="Featured" /> </SelectTrigger> <SelectContent> <SelectItem value="features" >Featured</SelectItem> <SelectItem value="recomended">Recomended</SelectItem> <SelectItem value="most popular">Most Popular</SelectItem> <SelectItem value="best value">Best value</SelectItem> </SelectContent> </Select> <div className='flex items center'> <Button className="bg white w fit p 3 rounded l sm rounded r none shadow none border border gray 200 hover:bg gray 100 duration 500 focus:bg gray 200"> <LayoutGrid className='fill black text black' /> </Button> <Button className="bg white w fit p 3 rounded r sm rounded l none shadow none border border gray 200 hover:bg gray 100 duration 500 focus:bg gray 200"> <Menu className='fill black text black' /> </Button> </div> </div> </div> <div className='grid gap 4'> {data.map((item, index) => ( <div className="flex flex col sm:flex row items start bg white rounded sm overflow hidden p 4 gap 4 w full border border gray 200" key={index} > <img className="w full sm:w 56 object cover rounded md" src={item.img} alt={item.title} /> <div className="flex flex col w full"> <div className="flex justify between items start"> <h2 className="text md font semibold text gray 800">{item.title}</h2> <Heart className="w 6 h 6 text blue 500 transition duration 300 cursor pointer" /> </div> <span className="flex items center gap 2 text gray 600 mt 2"> <p className="text xl font bold text blue 500 leading tight">${item.newPrice}.00</p> <p className="line through text sm font semibold text gray 400">${item.oldPrice}.00</p> </span> <div className="flex items center gap 2 mt "> <div className="flex text sm"> <AiFillStar className="w 4 h 4 fill amber 500" /> <AiFillStar className="w 4 h 4 fill amber 500" /> <AiFillStar className="w 4 h 4 fill amber 500" /> <AiFillStar className="w 4 h 4 fill amber 500" /> <AiFillStar className="w 4 h 4 fill gray 300" /> </div> <p className="text sm text gray 500">({item.rating})</p> <div className='size 2 bg gray 300 rounded full'></div> <p className="flex items center gap 1 text sm text gray 400"> <span className="text sm text gray 400">{item.ordered}</span> Orders </p> <div className='size 2 bg gray 300 rounded full'></div> <p className="flex items center gap 1 text sm text green 500"> <span className="">{item.shipping}</span> Shipping </p> </div> <p className="w 10/12 text sm text gray 600 mt 2">{item.description}</p> <p className="mt 4 text blue 500 text sm font semibold"> View Details </p> </div> </div> ))} {/* Pagination */} <div className='flex items end justify end'> <div className='flex items center gap 4'> <Select > <SelectTrigger className="w [130px] rounded sm border border gray 200 focus:ring 0 shadow none bg white"> <SelectValue placeholder="Show 10" /> </SelectTrigger> <SelectContent> <SelectItem value="show10" >Show 10</SelectItem> <SelectItem value="show20">Show 20</SelectItem> <SelectItem value="show30">Show 30</SelectItem> <SelectItem value="show40">Show 40</SelectItem> </SelectContent> </Select> <div className='flex items center bg white rounded sm cursor pointer'> <div className='w 9 h 9 border text gray 300 p 2 rounded l sm hover:bg gray 200 border gray 200 flex items center justify center'> <ChevronLeft /> </div> <div className='w 9 h 9 border bg gray 100 text gray 400 hover:bg gray 200 border gray 200 flex items center justify center'> 1 </div> <div className='w 9 h 9 border border gray 200 hover:bg gray 200 flex items center justify center'> 2 </div> <div className='w 9 h 9 border border gray 200 hover:bg gray 200 flex items center justify center'> 3 </div> <div className='w 9 h 9 p 2 border rounded r sm border gray 200 hover:bg gray 200 flex items center justify center'> <ChevronRight /> </div> </div> </div> </div> </div> </div> </div> </div> ) } list view is already set apply grid view and both change when clicked on button

Prompt
Component Preview

About

ProductListingPage - A dynamic product display with filters, ratings, and price range, professionally built with React and Tailwind. Copy template now!

Share

Last updated 1 month ago