Data
Not affiliated with official shadcn/ui. Visit ui.shadcn.com for official docs.
List Clean task lists with drag-and-drop organization for React and Next.js applications. Built with TypeScript support, shadcn/ui design, and smart grouping for simple yet powerful task management without complexity.
'use client'; import { faker } from '@faker-js/faker'; import type { DragEndEvent } from '@/components/ui/shadcn-io/list'; import { ListGroup, ListHeader, ListItem, ListItems, ListProvider, } from '@/components/ui/shadcn-io/list'; import { useState } from 'react'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); const statuses = [ { id: faker.string.uuid(), name: 'Planned', color: '#6B7280' }, { id: faker.string.uuid(), name: 'In Progress', color: '#F59E0B' }, { id: faker.string.uuid(), name: 'Done', color: '#10B981' }, ]; const users = Array.from({ length: 4 }) .fill(null) .map(() => ({ id: faker.string.uuid(), name: faker.person.fullName(), image: faker.image.avatar(), })); const exampleFeatures = Array.from({ length: 20 }) .fill(null) .map(() => ({ id: faker.string.uuid(), name: capitalize(faker.company.buzzPhrase()), startAt: faker.date.past({ years: 0.5, refDate: new Date() }), endAt: faker.date.future({ years: 0.5, refDate: new Date() }), status: faker.helpers.arrayElement(statuses), owner: faker.helpers.arrayElement(users), })); const Example = () => { const [features, setFeatures] = useState(exampleFeatures); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (!over) { return; } const status = statuses.find((status) => status.name === over.id); if (!status) { return; } setFeatures( features.map((feature) => { if (feature.id === active.id) { return { ...feature, status }; } return feature; }) ); }; return ( <ListProvider onDragEnd={handleDragEnd}> {statuses.map((status) => ( <ListGroup id={status.name} key={status.name}> <ListHeader color={status.color} name={status.name} /> <ListItems> {features .filter((feature) => feature.status.name === status.name) .map((feature, index) => ( <ListItem id={feature.id} index={index} key={feature.id} name={feature.name} parent={feature.status.name} > <div className="h-2 w-2 shrink-0 rounded-full" style={{ backgroundColor: feature.status.color }} /> <p className="m-0 flex-1 font-medium text-sm"> {feature.name} </p> {feature.owner && ( <Avatar className="h-4 w-4 shrink-0"> <AvatarImage src={feature.owner.image} /> <AvatarFallback> {feature.owner.name?.slice(0, 2)} </AvatarFallback> </Avatar> )} </ListItem> ))} </ListItems> </ListGroup> ))} </ListProvider> ); }; export default Example; 'use client'; import { DndContext, type DragEndEvent, rectIntersection, useDraggable, useDroppable, } from '@dnd-kit/core'; import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; import type { ReactNode } from 'react'; import { cn } from '@/lib/utils'; export type { DragEndEvent } from '@dnd-kit/core'; type Status = { id: string; name: string; color: string; }; type Feature = { id: string; name: string; startAt: Date; endAt: Date; status: Status; }; export type ListItemsProps = { children: ReactNode; className?: string; }; export const ListItems = ({ children, className }: ListItemsProps) => ( <div className={cn('flex flex-1 flex-col gap-2 p-3', className)}> {children} </div> ); export type ListHeaderProps = | { children: ReactNode; } | { name: Status['name']; color: Status['color']; className?: string; }; export const ListHeader = (props: ListHeaderProps) => 'children' in props ? ( props.children ) : ( <div className={cn( 'flex shrink-0 items-center gap-2 bg-foreground/5 p-3', props.className )} > <div className="h-2 w-2 rounded-full" style={{ backgroundColor: props.color }} /> <p className="m-0 font-semibold text-sm">{props.name}</p> </div> ); export type ListGroupProps = { id: Status['id']; children: ReactNode; className?: string; }; export const ListGroup = ({ id, children, className }: ListGroupProps) => { const { setNodeRef, isOver } = useDroppable({ id }); return ( <div className={cn( 'bg-secondary transition-colors', isOver && 'bg-foreground/10', className )} ref={setNodeRef} > {children} </div> ); }; export type ListItemProps = Pick<Feature, 'id' | 'name'> & { readonly index: number; readonly parent: string; readonly children?: ReactNode; readonly className?: string; }; export const ListItem = ({ id, name, index, parent, children, className, }: ListItemProps) => { const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({ id, data: { index, parent }, }); return ( <div className={cn( 'flex cursor-grab items-center gap-2 rounded-md border bg-background p-2 shadow-sm', isDragging && 'cursor-grabbing', className )} style={{ transform: transform ? `translateX(${transform.x}px) translateY(${transform.y}px)` : 'none', }} {...listeners} {...attributes} ref={setNodeRef} > {children ?? <p className="m-0 font-medium text-sm">{name}</p>} </div> ); }; export type ListProviderProps = { children: ReactNode; onDragEnd: (event: DragEndEvent) => void; className?: string; }; export const ListProvider = ({ children, onDragEnd, className, }: ListProviderProps) => ( <DndContext collisionDetection={rectIntersection} modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd} > <div className={cn('flex size-full flex-col', className)}>{children}</div> </DndContext> );
npx shadcn@latest add https://www.shadcn.io/registry/list.jsonnpx shadcn@latest add https://www.shadcn.io/registry/list.jsonpnpm dlx shadcn@latest add https://www.shadcn.io/registry/list.jsonbunx shadcn@latest add https://www.shadcn.io/registry/list.jsonSign in to access installation commands Sign in
Drag-and-drop reordering with precise item positioning using DND Kit for React applications
Smart grouping system organizing tasks by status, priority, or custom categories with TypeScript support
Clean visual hierarchy featuring color-coded groups and distraction-free design using shadcn/ui components
Minimal performance overhead optimized for instant feedback and smooth JavaScript interactions
Responsive design adapting seamlessly to any UI layout across all device sizes
TypeScript native with complete type safety and intelligent autocomplete for Next.js projects
Accessibility compliant including keyboard navigation and screen reader support
Open source with free MIT licensing for unlimited commercial and personal use
'use client'; import { faker } from '@faker-js/faker'; import type { DragEndEvent } from '@/components/ui/shadcn-io/list'; import { ListGroup, ListHeader, ListItem, ListItems, ListProvider, } from '@/components/ui/shadcn-io/list'; import { useState } from 'react'; const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); const statuses = [ { id: faker.string.uuid(), name: 'Planned', color: '#6B7280' }, { id: faker.string.uuid(), name: 'In Progress', color: '#F59E0B' }, { id: faker.string.uuid(), name: 'Done', color: '#10B981' }, ]; const users = Array.from({ length: 4 }) .fill(null) .map(() => ({ id: faker.string.uuid(), name: faker.person.fullName(), image: faker.image.avatar(), })); const exampleFeatures = Array.from({ length: 20 }) .fill(null) .map(() => ({ id: faker.string.uuid(), name: capitalize(faker.company.buzzPhrase()), startAt: faker.date.past({ years: 0.5, refDate: new Date() }), endAt: faker.date.future({ years: 0.5, refDate: new Date() }), status: faker.helpers.arrayElement(statuses), owner: faker.helpers.arrayElement(users), })); const Example = () => { const [features, setFeatures] = useState(exampleFeatures); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (!over) { return; } const status = statuses.find((status) => status.name === over.id); if (!status) { return; } setFeatures( features.map((feature) => { if (feature.id === active.id) { return { ...feature, status }; } return feature; }) ); }; return ( <ListProvider onDragEnd={handleDragEnd}> {statuses.map((status) => ( <ListGroup id={status.name} key={status.name}> <ListHeader color={status.color} name={status.name} /> <ListItems> {features .filter((feature) => feature.status.name === status.name) .map((feature, index) => ( <ListItem id={feature.id} index={index} key={feature.id} name={feature.name} parent={feature.status.name} /> ))} </ListItems> </ListGroup> ))} </ListProvider> ); }; export default Example; 'use client'; import { DndContext, type DragEndEvent, rectIntersection, useDraggable, useDroppable, } from '@dnd-kit/core'; import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; import type { ReactNode } from 'react'; import { cn } from '@/lib/utils'; export type { DragEndEvent } from '@dnd-kit/core'; type Status = { id: string; name: string; color: string; }; type Feature = { id: string; name: string; startAt: Date; endAt: Date; status: Status; }; export type ListItemsProps = { children: ReactNode; className?: string; }; export const ListItems = ({ children, className }: ListItemsProps) => ( <div className={cn('flex flex-1 flex-col gap-2 p-3', className)}> {children} </div> ); export type ListHeaderProps = | { children: ReactNode; } | { name: Status['name']; color: Status['color']; className?: string; }; export const ListHeader = (props: ListHeaderProps) => 'children' in props ? ( props.children ) : ( <div className={cn( 'flex shrink-0 items-center gap-2 bg-foreground/5 p-3', props.className )} > <div className="h-2 w-2 rounded-full" style={{ backgroundColor: props.color }} /> <p className="m-0 font-semibold text-sm">{props.name}</p> </div> ); export type ListGroupProps = { id: Status['id']; children: ReactNode; className?: string; }; export const ListGroup = ({ id, children, className }: ListGroupProps) => { const { setNodeRef, isOver } = useDroppable({ id }); return ( <div className={cn( 'bg-secondary transition-colors', isOver && 'bg-foreground/10', className )} ref={setNodeRef} > {children} </div> ); }; export type ListItemProps = Pick<Feature, 'id' | 'name'> & { readonly index: number; readonly parent: string; readonly children?: ReactNode; readonly className?: string; }; export const ListItem = ({ id, name, index, parent, children, className, }: ListItemProps) => { const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({ id, data: { index, parent }, }); return ( <div className={cn( 'flex cursor-grab items-center gap-2 rounded-md border bg-background p-2 shadow-sm', isDragging && 'cursor-grabbing', className )} style={{ transform: transform ? `translateX(${transform.x}px) translateY(${transform.y}px)` : 'none', }} {...listeners} {...attributes} ref={setNodeRef} > {children ?? <p className="m-0 font-medium text-sm">{name}</p>} </div> ); }; export type ListProviderProps = { children: ReactNode; onDragEnd: (event: DragEndEvent) => void; className?: string; }; export const ListProvider = ({ children, onDragEnd, className, }: ListProviderProps) => ( <DndContext collisionDetection={rectIntersection} modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd} > <div className={cn('flex size-full flex-col', className)}>{children}</div> </DndContext> );
This free open source React component works well for:
Personal productivity - Daily task management and todo lists with drag-and-drop organization
Team backlogs - Simple issue tracking and sprint planning for Next.js development teams
Content planning - Blog posts, social media campaigns, and editorial calendars with TypeScript safety
Customer feedback - Feature requests, bug reports, and user feedback grouped by priority
Learning paths - Course tracking, tutorial progress, and skill development using shadcn/ui design
Project organization - Resource lists, reading lists, and reference materials for JavaScript projects
DND Kit foundation provides vertical-only dragging with precise collision detection for React lists
TypeScript schemas support flexible data models from simple text to rich objects with metadata
Lightweight architecture focused on list functionality without complex project management overhead
Tailwind CSS integration using utility classes for consistent styling and easy customization
Performance optimized with fast loading, smooth interactions, and efficient state updates
Accessibility built-in featuring keyboard navigation, screen readers, and ARIA compliance
Complete keyboard support with shortcuts for power users and logical tab navigation
Screen reader optimized providing meaningful announcements for all drag-and-drop interactions
Focus management with clear indicators and high contrast mode compatibility
WCAG compliant ensuring React components work for all users across different abilities