List Users Block
The ListUsers Component is a fully customizable and accessible users table interface built with React, TypeScript, and MUI. It provides a complete tabular user listing experience with modern design patterns, sortable columns, tabs, status management, pagination support, loading states, and flexible customization options for advanced user management applications.
π Installationβ
npm install @nodeblocks/frontend-list-users-block@0.2.0
π Usageβ
import {ListUsers} from '@nodeblocks/frontend-list-users-block';
- Basic Usage
- Advanced Usage
function SimpleListUsers() { const users = [ { id: '1', name: 'John Doe', status: 'in_use', createdAt: '2023-01-15', }, { id: '2', name: 'Jane Smith', status: 'in_use', createdAt: '2023-02-20', }, { id: '3', name: 'Bob Johnson', status: 'not_in_use', createdAt: '2023-03-10', }, ]; const pagination = { currentPage: 1, totalPages: 1, onPageChange: newPage => console.log('Page:', newPage), }; const labels = { emptyStateMessage: 'No users found', headerRow: { createdAt: 'Created', name: 'Name', status: 'Status', }, cellData: { statusInUse: 'Active', statusNotInUse: 'Inactive', }, }; return ( <ListUsers listUsersTitle="Team Members" data={users} isLoading={false} pagination={pagination} onNavigate={href => console.log('Navigate:', href)} rowHref={row => `/users/${row.id}`} labels={labels} > <ListUsers.Header /> <ListUsers.Table /> </ListUsers> ); }
function AdvancedListUsers() { const [currentTab, setCurrentTab] = useState('All'); const [currentPage, setCurrentPage] = useState(1); const [isLoading] = useState(false); const allUsers = [ { id: '1', name: 'Alice Johnson', status: 'in_use', createdAt: '2023-01-15', }, { id: '2', name: 'Bob Williams', status: 'in_use', createdAt: '2023-02-20', }, { id: '3', name: 'Carol Davis', status: 'not_in_use', createdAt: '2023-03-10', }, { id: '4', name: 'David Brown', status: 'not_in_use', createdAt: '2023-04-05', }, { id: '5', name: 'Eve Miller', status: 'in_use', createdAt: '2023-05-12', }, ]; const filteredUsers = currentTab === 'All' ? allUsers : currentTab === 'Active' ? allUsers.filter(user => user.status === 'in_use') : allUsers.filter(user => user.status === 'not_in_use'); const tabs = [ {key: 'all', label: 'All', subtitle: `${allUsers.length} users`}, { key: 'active', label: 'Active', subtitle: `${allUsers.filter(u => u.status === 'in_use').length} users`, }, { key: 'inactive', label: 'Inactive', subtitle: `${allUsers.filter(u => u.status === 'not_in_use').length} users`, }, ]; const pagination = { currentPage, totalPages: Math.ceil(filteredUsers.length / 10), onPageChange: newPage => setCurrentPage(newPage), }; const labels = { emptyStateMessage: 'No users found', headerRow: { createdAt: 'Created At', name: 'User Name', status: 'Status', }, cellData: { statusInUse: 'Active', statusNotInUse: 'Inactive', }, }; return ( <ListUsers listUsersTitle="User Management" data={filteredUsers} isLoading={isLoading} pagination={pagination} onNavigate={href => console.log('Navigate:', href)} rowHref={row => `/users/${row.id}`} tabs={tabs} currentTab={currentTab} onTabChange={tab => { setCurrentTab(tab); setCurrentPage(1); }} labels={labels} sx={{ maxWidth: 1200, mx: 'auto', bgcolor: '#ffffff', borderRadius: 3, boxShadow: '0 4px 20px rgba(0,0,0,0.08)', overflow: 'hidden', }} > {({defaultBlocks, defaultBlockOrder}) => ({ blocks: { ...defaultBlocks, header: ( <ListUsers.Header sx={{ p: 3, borderBottom: '1px solid #e2e8f0', '& .nbb-list-users-title': { fontSize: 24, fontWeight: 700, color: '#1e293b', }, }} /> ), tabs: ( <ListUsers.Tabs sx={{ px: 3, borderBottom: '1px solid #e2e8f0', '& .MuiTab-root': { textTransform: 'none', fontWeight: 500, color: '#64748b', '&.Mui-selected': { color: '#6366f1', fontWeight: 600, }, }, '& .MuiTabs-indicator': { bgcolor: '#6366f1', height: 3, borderRadius: '3px 3px 0 0', }, }} /> ), table: ( <ListUsers.Table sx={{ '& .MuiTableHead-root': { bgcolor: '#f8fafc', '& .MuiTableCell-head': { color: '#475569', fontWeight: 600, textTransform: 'uppercase', fontSize: 12, letterSpacing: '0.05em', py: 2, }, }, '& .MuiTableBody-root': { '& .MuiTableRow-root': { transition: 'background 0.2s ease', '&:hover': { bgcolor: '#f8fafc', }, }, '& .MuiTableCell-body': { py: 2, color: '#334155', }, }, }} /> ), }, blockOrder: defaultBlockOrder, })} </ListUsers> ); }
π§ Props Referenceβ
Main Component Propsβ
| Prop | Type | Default | Description |
|---|---|---|---|
listUsersTitle | ReactNode | Required | Title for the users table section |
labels | LabelsConfig | Required | Labels object for table headers, status messages, and empty states |
data | UserData[] | Required | Array of user data objects |
isLoading | boolean | undefined | Whether the table is currently loading |
onNavigate | (to: string) => void | undefined | Callback function for navigation |
rowHref | (row: UserData) => string | undefined | Function to generate row link URLs |
tabs | TabConfig[] | undefined | Array of tab configuration objects |
currentTab | string | undefined | Currently active tab label |
onTabChange | (tab: string) => void | undefined | Callback function when tab is changed |
pagination | PaginationConfig | undefined | Pagination configuration object |
className | string | undefined | Additional CSS class name for styling the container |
children | BlocksOverride | undefined | Custom block components to override default rendering |
sx | SxProps | undefined | MUI sx prop for custom styling |
spacing | number | 3 | Spacing between child elements |
Note: This component extends MUI StackProps, inheriting all Stack component props.
Sub-Componentsβ
The ListUsers component provides several sub-components. All sub-components receive their default values from the main component's context and can override these values through props.
ListUsers.Titleβ
| Prop | Type | Default | Description |
|---|---|---|---|
listUsersTitle | ReactNode | From context | Title content to display |
children | ReactNode | From context | Custom content to override default title rendering |
className | string | undefined | Additional CSS class name for styling |
variant | string | "h4" | MUI Typography variant |
component | string | "h1" | HTML element to render |
Note: This component extends MUI TypographyProps.
ListUsers.Headerβ
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | undefined | Custom content to override default header rendering |
className | string | undefined | Additional CSS class name for styling |
Note: This component extends MUI BoxProps.
ListUsers.Contentβ
| Prop | Type | Default | Description |
|---|---|---|---|
isLoading | boolean | From context | Loading state from context |
children | ReactNode | undefined | Custom content to override default content rendering |
className | string | undefined | Additional CSS class name for styling |
Note: This component extends MUI BoxProps.
ListUsers.Loaderβ
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | <CircularProgress /> | Custom loading indicator content |
className | string | undefined | Additional CSS class name for styling |
sx | SxProps | undefined | MUI sx prop for custom styling |
Note: This component extends MUI StackProps with default alignItems: 'center'.
ListUsers.Tabsβ
| Prop | Type | Default | Description |
|---|---|---|---|
tabs | TabConfig[] | From context | Array of tab configuration objects |
currentTab | string | From context | Currently active tab label |
onTabChange | (tab: string) => void | From context | Tab change callback function |
className | string | undefined | Additional CSS class name for styling |
Note: This component extends MUI TabsProps (excluding value, onChange, variant). Uses variant="fullWidth" internally.
ListUsers.Tableβ
| Prop | Type | Default | Description |
|---|---|---|---|
data | UserData[] | From context | Array of table data |
labels | LabelsConfig | From context | Labels for table headers and status messages |
rowHref | (row: UserData) => string | From context | Function to generate row link URLs |
onNavigate | (to: string) => void | From context | Navigation callback function |
pagination | PaginationConfig | From context | Pagination configuration |
className | string | undefined | Additional CSS class name for styling |
spacing | number | 3 | Spacing between elements |
Note: This component extends MUI StackProps.
π¨ Configuration examplesβ
Custom Title Stylingβ
<ListUsers.Title
variant="h3"
sx={{
color: 'primary.main',
fontWeight: 'bold',
textTransform: 'uppercase',
}}
>
Custom User Title
</ListUsers.Title>
Pagination Setupβ
<ListUsers
listUsersTitle="Paginated Users"
labels={labels}
data={userData}
pagination={{
currentPage: 1,
totalPages: 10,
onPageChange: (page) => console.log('Page:', page),
}}
/>
Custom Empty State Messageβ
const labels = {
emptyStateMessage: 'No users match your search criteria',
headerRow: {
name: 'Full Name',
createdAt: 'Registration Date',
status: 'Account Status',
},
cellData: {
statusInUse: 'Active',
statusNotInUse: 'Inactive',
},
};
Block Override Patternβ
<ListUsers listUsersTitle="Users" labels={labels} data={userData}>
{({defaultBlocks, defaultBlockOrder}) => ({
blocks: {
...defaultBlocks,
header: (
<ListUsers.Header
sx={{
p: 3,
borderBottom: '1px solid #e2e8f0',
'& .nbb-list-users-title': {
fontSize: 24,
fontWeight: 700,
color: '#1e293b',
},
}}
/>
),
tabs: (
<ListUsers.Tabs
sx={{
px: 3,
borderBottom: '1px solid #e2e8f0',
'& .MuiTab-root': {
textTransform: 'none',
fontWeight: 500,
color: '#64748b',
'&.Mui-selected': {
color: '#6366f1',
fontWeight: 600,
},
},
}}
/>
),
table: (
<ListUsers.Table
sx={{
'& .MuiTableHead-root': {
bgcolor: '#f8fafc',
},
}}
/>
),
},
blockOrder: defaultBlockOrder,
})}
</ListUsers>
Custom Stylingβ
<ListUsers
listUsersTitle="Users"
data={users}
isLoading={false}
pagination={pagination}
onNavigate={console.log}
rowHref={row => `/users/${row.id}`}
labels={labels}
sx={{
bgcolor: 'grey.900',
color: 'grey.100',
borderRadius: 2,
}}
>
<ListUsers.Header
sx={{
p: 2,
borderBottom: '1px solid',
borderColor: 'grey.700',
'& .MuiTypography-root': {
color: 'grey.100',
},
}}
/>
<ListUsers.Table
sx={{
'& .MuiTableCell-root': {
color: 'grey.300',
borderColor: 'grey.700',
},
'& .MuiTableHead-root .MuiTableCell-root': {
color: 'grey.400',
bgcolor: 'grey.800',
},
}}
/>
</ListUsers>
Tabs with Subtitlesβ
const tabs = [
{ label: 'Active', subtitle: '(24)' },
{ label: 'Inactive', subtitle: '(8)' },
{ label: 'Pending', subtitle: '(3)', isDisabled: true },
];
<ListUsers tabs={tabs} currentTab="Active" onTabChange={setTab} /* ... */ />
π§ TypeScript Supportβ
Full TypeScript support with comprehensive type definitions:
import {ListUsers} from '@nodeblocks/frontend-list-users-block';
import {useState} from 'react';
interface UserData {
id: string;
name: string;
status: string;
createdAt: string;
}
interface PaginationConfig {
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
}
interface TabConfig {
key?: string;
label: string;
isDisabled?: boolean;
subtitle?: string;
}
interface LabelsConfig {
emptyStateMessage: string;
headerRow: {
createdAt: string;
name: string;
status: string;
};
cellData: {
statusInUse: string;
statusNotInUse: string;
};
}
function TypedListUsers() {
const userData: UserData[] = [
{
id: 'u1',
name: 'John Smith',
status: 'in_use',
createdAt: '2023-01-15',
},
{
id: 'u2',
name: 'Jane Doe',
status: 'in_use',
createdAt: '2023-02-20',
},
{
id: 'u3',
name: 'Mike Wilson',
status: 'not_in_use',
createdAt: '2023-03-10',
},
];
const paginationConfig: PaginationConfig = {
currentPage: 1,
totalPages: 1,
onPageChange: newPage => console.log('Page changed:', newPage),
};
const tabConfig: TabConfig[] = [
{key: 'all', label: 'All Users'},
{key: 'active', label: 'Active'},
{key: 'inactive', label: 'Inactive'},
];
const labelsConfig: LabelsConfig = {
emptyStateMessage: 'No team members found',
headerRow: {
createdAt: 'Joined',
name: 'Full Name',
status: 'Status',
},
cellData: {
statusInUse: 'Active',
statusNotInUse: 'Inactive',
},
};
const handleNavigate = (href: string): void => {
console.log('Navigating to:', href);
};
return (
<ListUsers
listUsersTitle="Team Management"
data={userData}
isLoading={false}
pagination={paginationConfig}
onNavigate={handleNavigate}
rowHref={(row: UserData) => `/users/${row.id}`}
tabs={tabConfig}
currentTab="All Users"
onTabChange={tab => console.log('Tab changed:', tab)}
labels={labelsConfig}
spacing={2}
sx={{
maxWidth: 1000,
mx: 'auto',
border: '1px solid #e5e7eb',
borderRadius: 2,
}}
>
<ListUsers.Header />
<ListUsers.Tabs />
<ListUsers.Table />
</ListUsers>
);
}
π Notesβ
- The component uses MUI's
Stackcomponent as its base, providing flexible layout options - Default spacing is
3(24px) and default padding isp: 3(24px) - All sub-components automatically receive context values from the main component
- Date formatting uses
luxonlibrary with formatyyyy/M/dfor display - Full datetime is shown on hover via
titleattribute - The
Tabscomponent uses MUI'svariant="fullWidth"internally - Empty state displays a person icon with the
emptyStateMessagefrom labels - Row hover effects are only applied when
rowHrefis provided - Clicking a row triggers
onNavigatewith the href returned byrowHref - Default blocks are:
title,header,content,tabs,table - CSS classes follow BEM-style naming:
nbb-list-users-container,nbb-list-users-title, etc. - The
Loadercomponent renders MUICircularProgressby default
Built with β€οΈ using React, TypeScript, and MUI.