
Syncfusion React Listview
- 354 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
Use syncfusion-react-listview for development tasks
About
syncfusion-react-listview: A skill for development. This provides functionality for development workflows.
- syncfusion-react-listview
Syncfusion React Listview by the numbers
- 354 all-time installs (skills.sh)
- +52 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,186 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-listviewAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 354 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
What it does
Use syncfusion-react-listview for development tasks
Files
Implementing Syncfusion React ListView
A comprehensive guide to implementing the Syncfusion React ListView component for displaying dynamic, interactive lists with advanced features like templating, filtering, selection, and data management.
When to Use This Skill
Use this skill when you need to:
- Display lists of items with rich templating and customization
- Implement single or multiple item selection
- Add, remove, or update list items dynamically
- Filter and search list data
- Create nested or grouped lists
- Handle user interactions (scroll, select, check)
- Apply custom animations and styling
- Support drag-and-drop operations
- Optimize performance with virtualization
- Implement data binding (local or remote)
- Add checkboxes or custom icons
- Provide accessibility features (WCAG 2.1)
Component Overview
The ListView component displays a collection of items in a scrollable, interactive list. It supports:
- Data Sources: Arrays, objects, DataManager, remote APIs
- Selection Modes: None, single, multiple, or checkbox-based
- Templates: Item templates, header templates, group templates
- Data Operations: Filtering, sorting, grouping, searching
- Performance: Virtual scrolling for 1000+ items
- Interactions: Click, select, scroll events
- Styling: Built-in themes, custom CSS, RTL support
- Accessibility: WCAG 2.1 Level AA compliant
Installation & Setup
Step 1: Install Syncfusion Packages
npm install @syncfusion/ej2-react-lists @syncfusion/ej2-base @syncfusion/ej2-dataStep 2: Import Required Modules
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
// For additional features:
import { Inject, Virtualization } from '@syncfusion/ej2-react-lists';Step 3: Import CSS
import '@syncfusion/ej2-react-lists/styles/material.css'; // or other themesQuick Start Example
import React from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
export function BasicListView() {
const data = [
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' },
{ id: '3', text: 'Item 3' }
];
return (
<ListViewComponent
id="list"
dataSource={data}
fields={{ text: 'text', id: 'id' }}
/>
);
}Core Concepts
1. Data Binding
Local Array:
const items = ['Apple', 'Banana', 'Orange'];
<ListViewComponent dataSource={items} />Object Array with Mapping:
const data = [
{ productId: 1, productName: 'Laptop', category: 'Electronics' },
{ productId: 2, productName: 'Desk', category: 'Furniture' }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'productId',
text: 'productName',
groupBy: 'category'
}}
/>2. Selection Handling
const handleSelect = (args) => {
console.log('Selected item:', args.text, args.id);
};
<ListViewComponent
dataSource={data}
select={handleSelect}
/>3. Item Management
- Add Items:
addItem(data, fields?) - Remove Items:
removeItem(item) - Update Items: Replace in dataSource and refresh
- Get Selection:
getSelectedItems()
4. Templates
// Item Template
const itemTemplate = (props) => (
<div className="e-list-wrapper">
<span>{props.text}</span>
<span className="e-list-content">{props.category}</span>
</div>
);
<ListViewComponent
dataSource={data}
fields={fields}
template={itemTemplate}
/>Documentation & Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation and imports
- Basic setup and first component
- CSS themes and styling
- Minimal working example
- Project structure
Data Binding & Rendering
📄 Read: references/data-binding-rendering.md
- Local array data sources
- Object mapping with fields
- Remote data with DataManager
- Query filtering on remote data
- Load on demand and pagination
- Dynamic data updates
Item Management (CRUD)
📄 Read: references/item-management.md
- Adding new items dynamically
- Removing items by reference
- Updating existing items
- Batch operations
- Finding items in list
- Managing nested list items
Selection & Filtering
📄 Read: references/selection-filtering.md
- Single item selection
- Multiple item selection
- Checkbox-based selection
- Programmatic selection
- Filtering list items
- Search functionality
- Selection events and callbacks
Templating & Customization
📄 Read: references/templating-customization.md
- Custom item templates (JSX, function, string)
- Header templates with
headerTemplate - Group header templates with
groupTemplate - Template data context and variables
- Dynamic templates based on device
- CSS customization with classes
- RTL template support
Advanced Features
📄 Read: references/advanced-features.md
- Grouping and sorting
- Nested lists with hierarchy (up to 3 levels)
- Checkbox state management
- Animations and effects
- Custom icons and image display
- Enable/disable item states
- Layout patterns for complex dashboards
Layout & Alignment Patterns ⭐ NEW
📄 Read: references/layout-alignment-patterns.md
- Patient Portal Pattern: ListView with Cards, Appointments, Prescriptions, Messages
- Appointments Dashboard: 3x2 Grid layout with filters, scheduler, analytics
- Monitoring Dashboard: 3x3 Grid layout with KPIs, alerts, tickets
- Fixing common alignment issues (scrolling, borders, spacing)
- Responsive grid layouts for mobile/tablet/desktop
- Complete code examples for each pattern
Performance & Virtualization
📄 Read: references/performance-virtualization.md
- Virtual scrolling for large datasets (1000+)
- Enabling virtualization
- Refresh item heights
- Memory optimization tips
- Combining with pagination
- Performance best practices
- Dataset size recommendations
API Reference & Properties
📄 Read: references/api-reference.md
- Complete property documentation
- Method signatures and parameters
- Event handler arguments
- Default values for all properties
- Property type specifications
- Usage examples for each API
Accessibility & Events
📄 Read: references/accessibility-events.md
- WCAG 2.1 compliance
- Keyboard navigation
- Screen reader support
- ARIA attributes
- Focus management
- Event lifecycle
- Event arguments and data
Common Patterns
Pattern 1: List with Selection
User clicks item → select event fires → update UI
const [selected, setSelected] = React.useState(null);
const handleSelect = (args) => {
setSelected(args);
};
<ListViewComponent
dataSource={items}
select={handleSelect}
/>Pattern 2: Add/Remove Items
const listViewRef = React.useRef(null);
const addItem = () => {
listViewRef.current.addItem([{ text: 'New Item', id: Date.now() }]);
};
const removeItem = (item) => {
listViewRef.current.removeItem(item);
};
<ListViewComponent ref={listViewRef} dataSource={items} />Pattern 3: Filtered List
const [filteredData, setFilteredData] = React.useState(items);
const handleFilter = (searchText) => {
const filtered = items.filter(item =>
item.text.toLowerCase().includes(searchText.toLowerCase())
);
setFilteredData(filtered);
};
<ListViewComponent dataSource={filteredData} />Pattern 4: Multiple Selection with Checkboxes
<ListViewComponent
dataSource={items}
showCheckBox={true}
fields={{ id: 'id', text: 'text', isChecked: 'checked' }}
/>Pattern 5: ListView in Card Container (NEW)
<div style={{
border: '1px solid #e0e0e0',
borderRadius: '4px',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{
padding: '16px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #e0e0e0'
}}>
<h3 style={{ margin: 0 }}>Messages</h3>
</div>
<div style={{ flex: 1, overflow: 'auto', minHeight: 0 }}>
<ListViewComponent
dataSource={items}
height="100%"
width="100%"
/>
</div>
</div>Pattern 6: ListView in Grid Layout (NEW)
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '16px'
}}>
{/* Card 1 - ListView */}
<div style={{ border: '1px solid #e0e0e0', display: 'flex', flexDirection: 'column' }}>
<div style={{ padding: '12px', backgroundColor: '#f5f5f5' }}>Appointments</div>
<div style={{ flex: 1, overflow: 'auto', minHeight: 0 }}>
<ListViewComponent dataSource={appointments} height="300px" />
</div>
</div>
{/* Card 2, Card 3, etc. */}
</div>Pattern 7: Dashboard with Multiple ListViews (NEW)
See Layout & Alignment Patterns Guide for:
- 🏥 Patient Portal: Appointments + Messages + Prescriptions grid
- 📅 Appointments Dashboard: 3x2 grid with filters and scheduler
- 📊 Monitoring Dashboard: 3x3 grid with KPIs, logs, alerts, and tickets
Key Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
dataSource | Array/DataManager | [] | Data to display |
fields | FieldSettingsModel | defaultMappedFields | Map data to fields |
template | string/function/JSX | null | Custom item template |
headerTemplate | string/function/JSX | null | Custom header template |
groupTemplate | string/function/JSX | null | Custom group template |
showCheckBox | boolean | false | Show checkboxes |
checkBoxPosition | 'Left'\ | 'Right' | 'Left' |
sortOrder | 'None'\ | 'Ascending'\ | 'Descending' |
enableVirtualization | boolean | false | Virtual scrolling |
height | number\ | string | '' |
width | number\ | string | '' |
cssClass | string | '' | Custom CSS class |
enabled | boolean | true | Enable/disable component |
enableRtl | boolean | false | Right-to-left support |
animation | AnimationSettings | {...} | Item animations |
Key Methods
| Method | Purpose |
|---|---|
addItem(data, fields?) | Add new items to list |
removeItem(item) | Remove item from list |
removeMultipleItems(items) | Remove multiple items at once |
selectItem(item) | Select an item |
selectMultipleItems(items) | Select multiple items |
unselectItem(item?) | Deselect item(s) |
getSelectedItems() | Get current selection |
findItem(item) | Find item details |
checkItem(item) | Check a checkbox item |
uncheckItem(item) | Uncheck checkbox item |
checkAllItems() | Check all items |
uncheckAllItems() | Uncheck all items |
enableItem(item) | Enable disabled item |
disableItem(item) | Disable item |
hideItem(item) | Hide item |
showItem(item) | Show hidden item |
back() | Navigate back from nested list |
refreshItemHeight() | Refresh item heights (virtualization) |
destroy() | Clean up component |
Key Events
| Event | Triggers When |
|---|---|
select | Item is selected |
actionBegin | Any action starts |
actionComplete | Any action completes |
actionFailure | Remote data fetch fails |
scroll | User scrolls to top/bottom |
Common Use Cases
1. Display Settings List: Grouped items with icons 2. Contact List: Multi-line templates with images 3. Nested Navigation: Drill-down hierarchy 4. Filterable Search: Dynamic data filtering 5. Todo List: Checkboxes and item removal 6. Product Catalog: Pagination and filtering 7. Chat Messages: Reverse grouping by date 8. Notification Feed: Scrolling with actions 9. Multi-Select Picker: Checkboxes and buttons 10. Hierarchical Menu: Nested items with back navigation
Tips & Best Practices
General Best Practices
- ✅ Always map
fieldscorrectly for data to display - ✅ Use templates for rich UI beyond plain text
- ✅ Enable virtualization for 1000+ items
- ✅ Use
DataManagerfor filtering/sorting large remote data - ✅ Memoize templates and callbacks to prevent re-renders
- ✅ Use
cssClassfor lightweight customization - ✅ Handle
selectevent for user interactions - ✅ Clean up with
destroy()when component unmounts - ❌ Don't render complex components in every item template
- ❌ Don't forget to set proper
fieldsmapping
Alignment & Layout Best Practices ⭐
- ✅ Always set explicit
heighton parent container when using flex layout - ✅ Use
overflow: 'auto'andminHeight: 0on flex children for scrolling - ✅ Use
flexShrink: 0on headers to prevent shrinking - ✅ Use
gapin templates instead of individual margins for consistent spacing - ✅ Set
width: '100%'andheight: '100%'on ListView to fill container - ✅ Wrap ListView in flex container with
display: 'flex'andflexDirection: 'column' - ✅ Use CSS Grid for dashboard layouts with proper
gridColumn/gridRow - ✅ Set
overflow: 'hidden'on card container to respect border-radius - ❌ Don't rely on default margins - they cause misalignment
- ❌ Don't skip flex container setup - ListView needs proper parent context
- ❌ Don't forget
minHeight: 0on scrolling containers in flex
Troubleshooting
Items not displaying?
- Verify
dataSourceis populated - Check
fieldsmapping matches data structure - Ensure
textfield is mapped correctly
Selection not working?
- Verify
selectevent handler is bound - Check item has valid
idfield - Ensure item is not disabled
Performance issues?
- Enable
enableVirtualization={true} - Reduce template complexity
- Use
DataManagerfor remote filtering instead of client-side
Styling issues?
- Import CSS theme file
- Check
cssClassis applied to root element - Inspect CSS specificity conflicts
---
Next Steps:
- Read appropriate reference files based on your use case
- Check API Reference for complete property/method documentation
- Review Accessibility guide for WCAG compliance
- Explore code examples for your specific feature
Accessibility & Events
Table of Contents
- WCAG 2.1 Compliance
- Keyboard Navigation
- Screen Reader Support
- ARIA Attributes
- Focus Management
- Event Lifecycle
- Complete Accessible Example
WCAG 2.1 Compliance
The Syncfusion ListView component is built to meet WCAG 2.1 Level AA standards.
Compliance Features
// 1. Semantic HTML Structure
// ListView renders proper list elements (<ul>, <li>)
// 2. Color Contrast
// All text meets 4.5:1 contrast ratio for normal text
// 3:1 for large text (18pt+)
// 3. Text Alternatives
// Icons have aria-label attributes
// 4. Keyboard Access
// All functionality accessible via keyboard
// 5. Focus Indicators
// Clear visible focus indicators on all interactive elements
<ListViewComponent
// Built-in accessibility features
htmlAttributes={{
role: 'list',
'aria-label': 'Navigation items'
}}
/>Accessibility Checklist
- ✅ Semantic HTML structure
- ✅ Proper heading hierarchy
- ✅ Color contrast compliance
- ✅ Keyboard navigation support
- ✅ Screen reader support
- ✅ Focus management
- ✅ ARIA labels and roles
- ✅ Touch target size (min 44x44px)
Keyboard Navigation
Default Keyboard Shortcuts
// Key Bindings:
// ↑ Arrow Up - Select previous item
// ↓ Arrow Down - Select next item
// Home - Select first item
// End - Select last item
// Enter/Space - Activate/Toggle checkbox
// Shift + Click - Multi-select range
// Ctrl + Click - Toggle select
// Tab - Move focus to next item (when focusable)
// Shift + Tab - Move focus to previous item
<ListViewComponent
// Keyboard navigation is enabled by default
/>Custom Keyboard Handler
import { useRef } from 'react';
export function KeyboardAccessibleListView() {
const listViewRef = useRef<ListViewComponent>(null);
const [selectedIndex, setSelectedIndex] = useState(0);
const handleKeyDown = (e: React.KeyboardEvent) => {
const data = listViewRef.current?.dataSource as any[];
switch (e.key) {
case 'ArrowUp':
e.preventDefault();
setSelectedIndex(Math.max(0, selectedIndex - 1));
listViewRef.current?.selectItem({ id: data[selectedIndex - 1]?.id });
break;
case 'ArrowDown':
e.preventDefault();
setSelectedIndex(Math.min(data.length - 1, selectedIndex + 1));
listViewRef.current?.selectItem({ id: data[selectedIndex + 1]?.id });
break;
case 'Home':
e.preventDefault();
setSelectedIndex(0);
listViewRef.current?.selectItem({ id: data[0]?.id });
break;
case 'End':
e.preventDefault();
setSelectedIndex(data.length - 1);
listViewRef.current?.selectItem({ id: data[data.length - 1]?.id });
break;
case 'Enter':
case ' ':
e.preventDefault();
// Handle activation
break;
}
};
return (
<div onKeyDown={handleKeyDown}>
<ListViewComponent ref={listViewRef} />
</div>
);
}Keyboard Navigation Interaction Matrix
| Key | Action | Precondition | Result |
|---|---|---|---|
| ↑ | Previous Item | Any item focused | Focus moves to previous item |
| ↓ | Next Item | Any item focused | Focus moves to next item |
| Home | First Item | ListView focused | Focus moves to first item |
| End | Last Item | ListView focused | Focus moves to last item |
| Enter | Select/Activate | Item focused | Item selected; select event fired |
| Space | Toggle Checkbox | Checkbox enabled | Checkbox toggled |
| Shift + ↓ | Range Select | showCheckBox=true | Multiple items selected |
| Ctrl + A | Select All | showCheckBox=true | All items selected |
Screen Reader Support
Screen Reader Testing
// Test with popular screen readers:
// - NVDA (Windows, open source)
// - JAWS (Windows, commercial)
// - VoiceOver (macOS, iOS, built-in)
// - TalkBack (Android, built-in)
export function ScreenReaderAccessibleListView() {
return (
<div role="application" aria-label="Task Management Application">
<h1>Tasks</h1>
<ListViewComponent
htmlAttributes={{
role: 'list',
'aria-label': 'Task list with checkboxes'
}}
headerTitle="My Tasks"
showHeader={true}
showCheckBox={true}
/>
</div>
);
}Announcements
import { useRef } from 'react';
export function ScreenReaderAnnouncementListView() {
const listViewRef = useRef<ListViewComponent>(null);
const [announcement, setAnnouncement] = useState('');
const handleSelect = (args: any) => {
// Announce to screen readers
setAnnouncement(`Selected: ${args.text}`);
};
return (
<div>
{/* Live region for screen reader announcements */}
<div
role="status"
aria-live="polite"
aria-atomic="true"
style={{ position: 'absolute', left: '-10000px' }}
>
{announcement}
</div>
<ListViewComponent
ref={listViewRef}
select={handleSelect}
/>
</div>
);
}ARIA Attributes
Core ARIA Labels
interface AccessibleListItem {
id: string;
text: string;
description: string;
disabled?: boolean;
checked?: boolean;
}
const data: AccessibleListItem[] = [
{
id: '1',
text: 'Inbox',
description: 'View all inbox messages',
disabled: false,
checked: false
}
];
export function AccessibleListViewWithARIA() {
const itemTemplate = (props: AccessibleListItem) => (
<div
role="listitem"
aria-label={`${props.text}. ${props.description}`}
aria-disabled={props.disabled}
aria-checked={props.checked}
>
<span>{props.text}</span>
<small>{props.description}</small>
</div>
);
return (
<ListViewComponent
dataSource={data}
template={itemTemplate}
htmlAttributes={{
role: 'list',
'aria-label': 'Navigation menu',
'aria-describedby': 'list-instructions'
}}
/>
);
}ARIA Attributes Reference
| Attribute | Value | Purpose |
|---|---|---|
role | 'list', 'listitem' | Define semantic role |
aria-label | String | Accessible label |
aria-labelledby | ID reference | Label by element ID |
aria-describedby | ID reference | Description by element ID |
aria-selected | true, false | Item selection state |
aria-checked | true, false, 'mixed' | Checkbox state |
aria-disabled | true, false | Item disabled state |
aria-readonly | true, false | Read-only state |
aria-live | 'polite', 'assertive' | Live region updates |
aria-atomic | true, false | Announce entire region |
aria-sort | 'ascending', 'descending', 'none' | Sort direction |
Focus Management
Tab Order and Focus
import { useRef } from 'react';
export function FocusManagementListView() {
const listViewRef = useRef<ListViewComponent>(null);
const addButtonRef = useRef<HTMLButtonElement>(null);
const handleKeyDown = (e: React.KeyboardEvent) => {
// Handle Tab key for custom focus management
if (e.key === 'Tab') {
const focusableElements = [
addButtonRef.current,
listViewRef.current?.element
];
const currentIndex = focusableElements.indexOf(e.target as any);
const nextIndex = e.shiftKey
? currentIndex - 1
: currentIndex + 1;
if (nextIndex >= 0 && nextIndex < focusableElements.length) {
focusableElements[nextIndex]?.focus();
}
}
};
return (
<div onKeyDown={handleKeyDown}>
<button ref={addButtonRef} aria-label="Add new item">
Add Item
</button>
<ListViewComponent
ref={listViewRef}
tabIndex={0}
htmlAttributes={{
'aria-label': 'Item list'
}}
/>
</div>
);
}Focus Indicators
// CSS for visible focus indicators
const focusStyles = `
.e-list-item:focus {
outline: 2px solid #2196F3;
outline-offset: 2px;
}
.e-list-item:focus-visible {
box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.3);
}
/* High contrast mode support */
@media (prefers-contrast: more) {
.e-list-item:focus {
outline: 3px solid currentColor;
}
}
`;
// Apply to component
<ListViewComponent
cssClass="accessible-list"
htmlAttributes={{
'data-focus-visible': 'true'
}}
/>Programmatic Focus
const handleMoveFocus = (direction: 'next' | 'prev') => {
const items = document.querySelectorAll('.e-list-item');
const currentIndex = Array.from(items).findIndex(
item => item === document.activeElement
);
let nextIndex = currentIndex + (direction === 'next' ? 1 : -1);
nextIndex = Math.max(0, Math.min(items.length - 1, nextIndex));
(items[nextIndex] as HTMLElement)?.focus();
};Event Lifecycle
Event Flow Diagram
User Interaction (click, keyboard, etc.)
↓
actionBegin fires
↓
Validation & Processing
↓
actionComplete fires (if successful)
OR
actionFailure fires (if error)
↓
After event handlers completeComplete Event Example
import { useRef, useState } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
export function EventLifecycleExample() {
const listViewRef = useRef<ListViewComponent>(null);
const [eventLog, setEventLog] = useState<string[]>([]);
const logEvent = (msg: string) => {
console.log(msg);
setEventLog(prev => [...prev, `${new Date().toLocaleTimeString()}: ${msg}`]);
};
const handleActionBegin = (args: any) => {
logEvent(`[actionBegin] eventType: ${args.eventType}`);
// Can cancel action here
if (args.eventType === 'select') {
if (args.data?.preventSelect) {
args.cancel = true;
logEvent('Selection cancelled');
}
}
};
const handleActionComplete = (args: any) => {
logEvent(`[actionComplete] eventType: ${args.eventType}`);
};
const handleActionFailure = (args: any) => {
logEvent(`[actionFailure] Error: ${args.error}`);
};
const handleSelect = (args: any) => {
logEvent(`[select] Selected: ${args.text}`);
};
const handleScroll = (args: any) => {
logEvent(
`[scroll] scrollTop: ${args.scrollTop}, isAtEnd: ${args.isAtEnd}`
);
};
const data = [
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' },
{ id: '3', text: 'Item 3' }
];
return (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
<div>
<h3>ListView</h3>
<ListViewComponent
ref={listViewRef}
dataSource={data}
height="300px"
actionBegin={handleActionBegin}
actionComplete={handleActionComplete}
actionFailure={handleActionFailure}
select={handleSelect}
scroll={handleScroll}
/>
</div>
<div>
<h3>Event Log</h3>
<div
style={{
border: '1px solid #ddd',
padding: '10px',
height: '300px',
overflowY: 'auto',
fontSize: '12px',
fontFamily: 'monospace'
}}
>
{eventLog.map((log, idx) => (
<div key={idx}>{log}</div>
))}
</div>
</div>
</div>
);
}Complete Accessible Example
import { useRef, useState } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
interface AccessibleItem {
id: string;
text: string;
description: string;
completed: boolean;
}
export function CompleteAccessibleListView() {
const listViewRef = useRef<ListViewComponent>(null);
const [items, setItems] = useState<AccessibleItem[]>([
{
id: '1',
text: 'Review documentation',
description: 'WCAG 2.1 compliance guidelines',
completed: false
},
{
id: '2',
text: 'Implement keyboard support',
description: 'Arrow keys, Tab, Enter functionality',
completed: true
},
{
id: '3',
text: 'Test with screen readers',
description: 'NVDA, JAWS, VoiceOver compatibility',
completed: false
}
]);
const [announcement, setAnnouncement] = useState('');
const handleSelect = (args: any) => {
const item = args.data as AccessibleItem;
setAnnouncement(
`Selected ${item.text}. ${item.completed ? 'Completed' : 'Incomplete'}`
);
};
const handleToggleComplete = (itemId: string) => {
setItems(items.map(item =>
item.id === itemId ? { ...item, completed: !item.completed } : item
));
const item = items.find(i => i.id === itemId);
setAnnouncement(
`${item?.text} marked as ${!item?.completed ? 'completed' : 'incomplete'}`
);
};
const itemTemplate = (props: AccessibleItem) => (
<div
style={{
padding: '12px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
borderBottom: '1px solid #f0f0f0'
}}
>
<div>
<div
style={{
textDecoration: props.completed ? 'line-through' : 'none',
fontWeight: '500',
marginBottom: '4px'
}}
>
{props.text}
</div>
<div
style={{
fontSize: '13px',
color: '#666',
marginBottom: '8px'
}}
>
{props.description}
</div>
</div>
<button
onClick={() => handleToggleComplete(props.id)}
aria-label={`Toggle ${props.text} completion status`}
aria-pressed={props.completed}
style={{
padding: '4px 8px',
minWidth: '44px',
minHeight: '44px',
backgroundColor: props.completed ? '#4CAF50' : '#e0e0e0',
color: props.completed ? 'white' : '#333',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '13px',
fontWeight: '500'
}}
>
{props.completed ? '✓' : 'O'}
</button>
</div>
);
return (
<div style={{ maxWidth: '800px' }}>
{/* Live region for announcements */}
<div
role="status"
aria-live="polite"
aria-atomic="true"
style={{
position: 'absolute',
left: '-10000px',
width: '1px',
height: '1px',
overflow: 'hidden'
}}
>
{announcement}
</div>
<div
style={{
marginBottom: '20px',
padding: '15px',
backgroundColor: '#f5f5f5',
borderRadius: '4px'
}}
>
<h2 style={{ marginTop: 0 }}>Accessible Task List</h2>
<div id="list-instructions" style={{ fontSize: '14px', color: '#666' }}>
<p>Use arrow keys to navigate, Enter to select, Space to toggle checkbox.</p>
<p>
<strong>Keyboard Shortcuts:</strong>
</p>
<ul style={{ margin: '5px 0' }}>
<li>↑ / ↓ - Navigate between items</li>
<li>Home / End - Jump to first/last item</li>
<li>Enter - Select item</li>
<li>Space - Toggle completion</li>
</ul>
</div>
</div>
<ListViewComponent
ref={listViewRef}
dataSource={items}
template={itemTemplate}
select={handleSelect}
height="400px"
htmlAttributes={{
role: 'list',
'aria-label': 'Task list with completion status',
'aria-describedby': 'list-instructions'
}}
/>
<div
style={{
marginTop: '15px',
padding: '10px',
backgroundColor: '#e3f2fd',
borderRadius: '4px',
fontSize: '12px'
}}
>
<strong>Accessibility Features:</strong>
<ul style={{ margin: '5px 0', paddingLeft: '20px' }}>
<li>✓ WCAG 2.1 Level AA compliant</li>
<li>✓ Full keyboard navigation support</li>
<li>✓ Screen reader compatible</li>
<li>✓ ARIA labels and live regions</li>
<li>✓ Focus indicators visible</li>
<li>✓ Min 44x44px touch targets</li>
<li>✓ High contrast mode support</li>
</ul>
</div>
</div>
);
}Testing for Accessibility
Automated Testing
// Install jest-axe for accessibility testing
// npm install jest-axe
import { axe, toHaveNoViolations } from 'jest-axe';
import { render } from '@testing-library/react';
expect.extend(toHaveNoViolations);
test('ListView should have no accessibility violations', async () => {
const { container } = render(
<ListViewComponent
dataSource={[{ id: '1', text: 'Item' }]}
/>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});Manual Testing Checklist
- [ ] Keyboard navigation works (arrow keys, Tab, Enter)
- [ ] Focus indicators visible on all interactive elements
- [ ] Screen reader announces items and their state
- [ ] Color contrast meets 4.5:1 ratio (normal text)
- [ ] All functionality available without mouse
- [ ] Touch targets at least 44x44px
- [ ] Form labels associated with inputs
- [ ] Error messages clearly described
- [ ] Animation doesn't prevent content access
- [ ] Works with browser zoom at 200%
Advanced Features
Table of Contents
- Grouping and Sorting
- Nested Lists
- Checkboxes & States
- Animations
- Icons and Images
- Hyperlink Navigation
- Item Disable/Enable
Grouping and Sorting
Group by Field
interface GroupedItem {
id: string;
text: string;
category: string;
}
const data: GroupedItem[] = [
{ id: '1', text: 'Apple', category: 'Fruit' },
{ id: '2', text: 'Carrot', category: 'Vegetable' },
{ id: '3', text: 'Banana', category: 'Fruit' },
{ id: '4', text: 'Tomato', category: 'Vegetable' }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'text',
groupBy: 'category' // ← Groups by category
}}
/>Sorting Options
// Ascending sort
<ListViewComponent
dataSource={data}
sortOrder="Ascending"
/>
// Descending sort
<ListViewComponent
dataSource={data}
sortOrder="Descending"
/>
// No sort
<ListViewComponent
dataSource={data}
sortOrder="None"
/>Sort with Field Mapping
const data = [
{ id: '1', name: 'Charlie', sortKey: 'c' },
{ id: '2', name: 'Alice', sortKey: 'a' },
{ id: '3', name: 'Bob', sortKey: 'b' }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'name',
sortBy: 'sortKey' // Sort by sortKey, not name
}}
sortOrder="Ascending"
/>Nested Lists
Hierarchical Data Structure
interface TreeItem {
id: string;
text: string;
child?: TreeItem[];
}
const hierarchyData: TreeItem[] = [
{
id: '1',
text: 'Fruits',
child: [
{ id: '1-1', text: 'Apple' },
{ id: '1-2', text: 'Banana' },
{ id: '1-3', text: 'Orange' }
]
},
{
id: '2',
text: 'Vegetables',
child: [
{ id: '2-1', text: 'Carrot' },
{ id: '2-2', text: 'Tomato' },
{ id: '2-3', text: 'Lettuce' }
]
}
];
<ListViewComponent
dataSource={hierarchyData}
fields={{
id: 'id',
text: 'text',
child: 'child' // ← Specify child items field
}}
/>Navigation Between Nested Lists
import { useRef } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
export function NestedListView() {
const listViewRef = useRef<ListViewComponent>(null);
const handleSelect = (args: any) => {
// When user selects a parent item with children
if (args.data?.child && args.data.child.length > 0) {
// Navigate to child list (happens automatically in ListView)
console.log('Navigated to:', args.text);
}
};
const handleBack = () => {
// Go back to parent list
listViewRef.current?.back?.();
};
return (
<div>
<button onClick={handleBack} style={{ marginBottom: '10px' }}>
← Back
</button>
<ListViewComponent
ref={listViewRef}
dataSource={hierarchyData}
fields={{
id: 'id',
text: 'text',
child: 'child'
}}
select={handleSelect}
/>
</div>
);
}Three-Level Hierarchy
const threeLevel: TreeItem[] = [
{
id: '1',
text: 'Electronics',
child: [
{
id: '1-1',
text: 'Computers',
child: [
{ id: '1-1-1', text: 'Laptop' },
{ id: '1-1-2', text: 'Desktop' }
]
},
{
id: '1-2',
text: 'Phones',
child: [
{ id: '1-2-1', text: 'Smartphone' },
{ id: '1-2-2', text: 'Tablet' }
]
}
]
}
];Checkboxes & States
Checkbox States
interface CheckableItem {
id: string;
text: string;
checked?: boolean;
}
const data: CheckableItem[] = [
{ id: '1', text: 'Item 1', checked: true },
{ id: '2', text: 'Item 2', checked: false },
{ id: '3', text: 'Item 3', checked: true }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'text',
isChecked: 'checked'
}}
showCheckBox={true}
/>Manage Checkbox States
import { useRef } from 'react';
export function CheckboxManagementExample() {
const listViewRef = useRef<ListViewComponent>(null);
const [items, setItems] = useState(data);
const handleCheckAll = () => {
listViewRef.current?.checkAllItems?.();
const updated = items.map(item => ({ ...item, checked: true }));
setItems(updated);
};
const handleUncheckAll = () => {
listViewRef.current?.uncheckAllItems?.();
const updated = items.map(item => ({ ...item, checked: false }));
setItems(updated);
};
const getCheckedItems = () => {
const selected = listViewRef.current?.getSelectedItems?.();
console.log('Checked items:', selected);
};
return (
<div>
<button onClick={handleCheckAll}>Check All</button>
<button onClick={handleUncheckAll}>Uncheck All</button>
<button onClick={getCheckedItems}>Get Checked</button>
<ListViewComponent
ref={listViewRef}
dataSource={items}
showCheckBox={true}
checkBoxPosition="Left"
/>
</div>
);
}Animations
Apply Animations
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
export function AnimatedListView() {
const data = [
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
];
return (
<ListViewComponent
dataSource={data}
animation={{
effect: 'Zoom', // Zoom, FadeIn, SlideLeft, SlideRight, etc.
duration: 500, // milliseconds
easing: 'ease-in-out'
}}
/>
);
}Animation Options
// Available effects:
const effects = [
'None',
'SlideLeft',
'SlideRight',
'SlideUp',
'SlideDown',
'FadeIn',
'Zoom',
'Expand'
];
// Easing options:
const easings = [
'ease',
'ease-in',
'ease-out',
'ease-in-out',
'linear'
];
// Example with different effect
<ListViewComponent
animation={{
effect: 'SlideDown',
duration: 400,
easing: 'ease-out'
}}
/>Icons and Images
Show Icons
interface IconItem {
id: string;
text: string;
icon: string; // CSS class name
}
const data: IconItem[] = [
{ id: '1', text: 'Inbox', icon: 'e-icons e-mail' },
{ id: '2', text: 'Sent', icon: 'e-icons e-send' },
{ id: '3', text: 'Drafts', icon: 'e-icons e-edit' },
{ id: '4', text: 'Trash', icon: 'e-icons e-delete' }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'text',
iconCss: 'icon'
}}
showIcon={true}
/>Display Images
interface ImageItem {
id: string;
text: string;
image: string; // URL to image
}
const data: ImageItem[] = [
{ id: '1', text: 'Profile 1', image: '/images/profile1.jpg' },
{ id: '2', text: 'Profile 2', image: '/images/profile2.jpg' }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'text',
image: 'image'
}}
/>Custom Icon Template
const iconTemplate = (props: any) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
<span style={{
width: '30px',
height: '30px',
backgroundColor: props.color,
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
marginRight: '10px',
fontWeight: 'bold'
}}>
{props.initials}
</span>
<span>{props.text}</span>
</div>
);
const data = [
{ id: '1', text: 'Alice Johnson', initials: 'AJ', color: '#2196F3' },
{ id: '2', text: 'Bob Smith', initials: 'BS', color: '#4CAF50' }
];
<ListViewComponent
dataSource={data}
template={iconTemplate}
/>Item Disable/Enable
Disable Specific Items
interface DisableableItem {
id: string;
text: string;
enabled?: boolean;
}
const data: DisableableItem[] = [
{ id: '1', text: 'Item 1', enabled: true },
{ id: '2', text: 'Item 2 (Disabled)', enabled: false },
{ id: '3', text: 'Item 3', enabled: true }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'text',
enabled: 'enabled'
}}
/>Enable/Disable Programmatically
const handleDisableItem = (itemId: string) => {
const item = { id: itemId };
listViewRef.current?.disableItem(item);
};
const handleEnableItem = (itemId: string) => {
const item = { id: itemId };
listViewRef.current?.enableItem(item);
};Hide/Show Items
interface VisibleItem {
id: string;
text: string;
visible?: boolean;
}
const data: VisibleItem[] = [
{ id: '1', text: 'Visible Item', visible: true },
{ id: '2', text: 'Hidden Item', visible: false }
];
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'text',
isVisible: 'visible'
}}
/>Toggle Visibility
const handleHideItem = (itemId: string) => {
const item = { id: itemId };
listViewRef.current?.hideItem(item);
};
const handleShowItem = (itemId: string) => {
const item = { id: itemId };
listViewRef.current?.showItem(item);
};Complete Advanced Example
import { useRef, useState } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
interface AdvancedItem {
id: string;
text: string;
category: string;
icon: string;
enabled: boolean;
checked?: boolean;
}
export function CompleteAdvancedListView() {
const listViewRef = useRef<ListViewComponent>(null);
const [view, setView] = useState('grid');
const data: AdvancedItem[] = [
{ id: '1', text: 'High Priority', category: 'Work', icon: 'e-icons e-flag', enabled: true, checked: false },
{ id: '2', text: 'Meeting Notes', category: 'Work', icon: 'e-icons e-notes', enabled: true, checked: true },
{ id: '3', text: 'Vacation Plans', category: 'Personal', icon: 'e-icons e-calendar', enabled: false, checked: false }
];
const itemTemplate = (props: AdvancedItem) => (
<div style={{
display: 'flex',
alignItems: 'center',
padding: '10px',
opacity: props.enabled ? 1 : 0.5,
pointerEvents: props.enabled ? 'auto' : 'none'
}}>
<i className={props.icon} style={{ marginRight: '10px', color: '#2196F3' }}></i>
<div style={{ flex: 1 }}>
<div>{props.text}</div>
<div style={{ fontSize: '12px', color: '#999' }}>{props.category}</div>
</div>
{props.checked && <span>✓</span>}
</div>
);
return (
<div>
<div style={{ marginBottom: '15px' }}>
<button onClick={() => listViewRef.current?.checkAllItems?.()}>
Check All
</button>
<button onClick={() => listViewRef.current?.uncheckAllItems?.()}>
Uncheck All
</button>
</div>
<ListViewComponent
ref={listViewRef}
dataSource={data}
fields={{
id: 'id',
text: 'text',
groupBy: 'category',
iconCss: 'icon',
enabled: 'enabled',
isChecked: 'checked'
}}
template={itemTemplate}
showCheckBox={true}
sortOrder="Ascending"
animation={{
effect: 'Zoom',
duration: 300,
easing: 'ease-in-out'
}}
height="400px"
/>
</div>
);
}Layout Patterns for Complex UI
Patient Portal Pattern: Message List in Card
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
interface Message {
id: string;
senderName: string;
subject: string;
date: string;
isRead: boolean;
}
export function PatientPortalMessagePanel() {
const messagesData: Message[] = [
{ id: '1', senderName: 'Dr. Sarah Johnson', subject: 'Lab Results Review', date: '2026-05-29', isRead: true },
{ id: '2', senderName: 'Dr. Michael Chen', subject: 'Appointment Reminder', date: '2026-05-28', isRead: false },
{ id: '3', senderName: 'Billing Department', subject: 'Payment Confirmation', date: '2026-05-27', isRead: true }
];
const messageTemplate = (props: Message) => (
<div style={{
display: 'flex',
flexDirection: 'column',
padding: '12px 16px',
borderBottom: '1px solid #f0f0f0',
backgroundColor: props.isRead ? 'white' : '#e3f2fd',
gap: '4px',
borderLeft: `4px solid ${props.isRead ? 'transparent' : '#2196F3'}`
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontWeight: 600, color: '#333' }}>{props.senderName}</span>
<span style={{ fontSize: '12px', color: '#999' }}>{props.date}</span>
</div>
<div style={{ fontSize: '14px', color: '#666' }}>
{props.subject}
</div>
</div>
);
return (
<div style={{
border: '1px solid #e0e0e0',
borderRadius: '4px',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{
padding: '16px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #e0e0e0',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<h3 style={{ margin: 0 }}>Message Your Provider</h3>
<span style={{
display: 'inline-block',
width: '24px',
height: '24px',
backgroundColor: '#f44336',
color: 'white',
borderRadius: '50%',
textAlign: 'center',
lineHeight: '24px',
fontSize: '12px',
fontWeight: 'bold'
}}>
3
</span>
</div>
<div style={{ flex: 1, overflow: 'hidden', minHeight: 0 }}>
<ListViewComponent
dataSource={messagesData}
fields={{ id: 'id', text: 'subject' }}
template={messageTemplate}
height="300px"
width="100%"
/>
</div>
</div>
);
}Dashboard Grid Layout: 3x2 with ListViews
interface Appointment {
id: string;
doctorName: string;
time: string;
status: 'CONFIRMED' | 'PENDING';
}
interface Alert {
id: string;
type: 'WARNING' | 'INFO' | 'ERROR';
message: string;
time: string;
}
interface Ticket {
id: string;
ticketNo: string;
priority: 'HIGH' | 'MEDIUM' | 'LOW';
status: string;
}
export function MonitoringDashboard() {
const appointmentsData: Appointment[] = [
{ id: '1', doctorName: 'Dr. Sarah', time: '09:00 AM', status: 'CONFIRMED' },
{ id: '2', doctorName: 'Dr. Brown', time: '03:30 PM', status: 'PENDING' }
];
const alertsData: Alert[] = [
{ id: '1', type: 'WARNING', message: 'Memory approaching 90% on server-03', time: '15 minutes ago' },
{ id: '2', type: 'INFO', message: 'Deployment completed successfully', time: '1 hour ago' }
];
const ticketsData: Ticket[] = [
{ id: '1', ticketNo: 'TICK-1234', priority: 'HIGH', status: 'Login page loading slow' },
{ id: '2', ticketNo: 'TICK-5678', priority: 'MEDIUM', status: 'Dashboard performance issue' },
{ id: '3', ticketNo: 'TICK-9012', priority: 'LOW', status: 'Export feature not working' }
];
const appointmentTemplate = (props: Appointment) => (
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '12px 16px',
borderBottom: '1px solid #f0f0f0'
}}>
<div>
<div style={{ fontWeight: 600 }}>{props.doctorName}</div>
<div style={{ fontSize: '12px', color: '#666' }}>{props.time}</div>
</div>
<span style={{
padding: '4px 8px',
borderRadius: '3px',
fontSize: '11px',
backgroundColor: props.status === 'CONFIRMED' ? '#c8e6c9' : '#fff9c4',
color: props.status === 'CONFIRMED' ? '#2e7d32' : '#f57f17'
}}>
{props.status}
</span>
</div>
);
const alertTemplate = (props: Alert) => (
<div style={{
padding: '12px 16px',
borderBottom: '1px solid #f0f0f0',
borderLeft: `4px solid ${props.type === 'WARNING' ? '#ff9800' : props.type === 'ERROR' ? '#f44336' : '#2196f3'}`,
backgroundColor: props.type === 'WARNING' ? '#fff8e1' : props.type === 'ERROR' ? '#ffebee' : '#e3f2fd'
}}>
<div style={{ fontWeight: 600, fontSize: '12px', marginBottom: '4px' }}>{props.type}</div>
<div style={{ fontSize: '13px', marginBottom: '4px' }}>{props.message}</div>
<div style={{ fontSize: '11px', color: '#999' }}>{props.time}</div>
</div>
);
const ticketTemplate = (props: Ticket) => (
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '12px 16px',
borderBottom: '1px solid #f0f0f0'
}}>
<div>
<div style={{ fontWeight: 600, color: '#2196F3' }}>{props.ticketNo}</div>
<div style={{ fontSize: '12px', color: '#666' }}>{props.status}</div>
</div>
<span style={{
padding: '4px 8px',
borderRadius: '3px',
fontSize: '10px',
fontWeight: 'bold',
backgroundColor: props.priority === 'HIGH' ? '#ffcdd2' : props.priority === 'MEDIUM' ? '#ffe0b2' : '#c8e6c9',
color: props.priority === 'HIGH' ? '#c62828' : props.priority === 'MEDIUM' ? '#e65100' : '#2e7d32'
}}>
{props.priority}
</span>
</div>
);
const cardContainerStyle = {
border: '1px solid #e0e0e0',
borderRadius: '4px',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column' as const
};
const cardHeaderStyle = {
padding: '12px 16px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #e0e0e0',
fontWeight: 600
};
const cardBodyStyle = {
flex: 1,
overflow: 'hidden',
minHeight: 0
};
return (
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '16px',
padding: '16px'
}}>
{/* Top Row */}
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Today's Appointments</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={appointmentsData}
template={appointmentTemplate}
height="200px"
width="100%"
/>
</div>
</div>
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Quick Stats</div>
<div style={{ padding: '16px' }}>
{/* Stats Cards */}
</div>
</div>
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Filter by Doctor</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={[{ id: '1', text: 'Dr. Sarah Smith' }]}
height="200px"
/>
</div>
</div>
{/* Middle Row */}
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Deployment History</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={[]}
height="200px"
/>
</div>
</div>
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Active Alerts</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={alertsData}
template={alertTemplate}
height="200px"
width="100%"
/>
</div>
</div>
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Open Tickets</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={ticketsData}
template={ticketTemplate}
height="200px"
width="100%"
/>
</div>
</div>
</div>
);
}Key Alignment Fixes
Problems Fixed: 1. ✅ ListView not filling container - Set height: 100% and wrap in flex container 2. ✅ Items not aligned properly - Use flexbox with gap instead of individual margins 3. ✅ Scrolling issues - Set overflow: auto and minHeight: 0 on flex container 4. ✅ Border alignment - Remove default padding, use explicit template padding 5. ✅ Multi-column layout alignment - Use CSS Grid with proper row/column spanning 6. ✅ Card containers misaligned - Use flexDirection: 'column' with flex children 7. ✅ Header not staying fixed - Use flexShrink: 0 for non-scrollable elements 8. ✅ Content overflow - Set parent container overflow: hidden and child overflow: auto
CSS Class for Alignment:
/* Add to your global CSS for consistent ListView styling */
.listview-card {
border: 1px solid #e0e0e0;
border-radius: 4px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.listview-card-header {
padding: 12px 16px;
background-color: #f5f5f5;
border-bottom: 1px solid #e0e0e0;
flex-shrink: 0;
}
.listview-card-body {
flex: 1;
overflow: auto;
min-height: 0;
}
.listview-item-aligned {
display: flex;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
gap: 12px;
box-sizing: border-box;
}API Reference
Table of Contents
Properties
animation
Type: AnimationSettings Default: { effect: 'None', duration: 400, easing: 'ease-in' }
Controls item animation when rendered or updated.
<ListViewComponent
animation={{
effect: 'SlideDown', // FadeIn, SlideUp, SlideDown, SlideLeft, SlideRight, Zoom
duration: 500, // milliseconds
easing: 'ease-out' // ease, ease-in, ease-out, ease-in-out, linear
}}
/>checkBoxPosition
Type: 'Left' | 'Right' Default: 'Left'
Position of checkbox when showCheckBox is enabled.
// Checkbox on left
<ListViewComponent checkBoxPosition="Left" showCheckBox={true} />
// Checkbox on right
<ListViewComponent checkBoxPosition="Right" showCheckBox={true} />cssClass
Type: string Default: ''
CSS class applied to the ListView container.
<ListViewComponent
cssClass="my-custom-list e-primary"
/>
// In CSS:
// .my-custom-list { padding: 20px; }
// .my-custom-list .e-list-item { color: blue; }dataSource
Type: any[] | DataManager Default: []
Data to display in the ListView.
// Array of objects
<ListViewComponent
dataSource={[
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' }
]}
/>
// DataManager for remote data
const dm = new DataManager({
url: 'https://api.example.com/items',
adaptor: new UrlAdaptor()
});
<ListViewComponent dataSource={dm} />disableHtmlEncode
Type: boolean Default: true
Enable rendering of raw text content in the ListView component without HTML encoding. When set to true, the text will be displayed exactly as provided (including HTML tags or special characters), instead of being encoded or truncated.
Note: To preserve and render raw HTML content correctly, enableHtmlSanitizer must also be set to false.
// Display raw HTML content (both enableHtmlSanitizer=false AND disableHtmlEncode=true)
<ListViewComponent
disableHtmlEncode={true}
enableHtmlSanitizer={false}
template={(props: any) => `<div><strong>${props.text}</strong></div>`}
dataSource={[
{ id: 1, text: 'Product <em>Name</em>' },
{ id: 2, text: 'Price: $99.99' }
]}
/>
// With HTML encoding (default behavior)
<ListViewComponent
disableHtmlEncode={false}
enableHtmlSanitizer={true}
dataSource={[
{ id: 1, text: 'Item with <tag>' }, // Will show as: Item with <tag>
{ id: 2, text: 'Text & symbols' } // Will show as: Text & symbols
]}
/>
// Display special characters as-is
<ListViewComponent
disableHtmlEncode={true}
dataSource={[
{ id: 1, text: 'hiiih<hihi' }, // Shows exactly as: hiiih<hihi
{ id: 2, text: 'Price: $50 & up' }
]}
/>enable
Type: boolean Default: true
Enable or disable the ListView component.
<ListViewComponent enable={isEnabled} />enableHtmlSanitizer
Type: boolean Default: true
Sanitize HTML in templates to prevent XSS attacks.
// Safe: HTML is sanitized
<ListViewComponent
enableHtmlSanitizer={true}
template={(props: any) => `<div>${props.text}</div>`}
/>
// Unsafe: Allows any HTML
<ListViewComponent
enableHtmlSanitizer={false}
template={(props: any) => `<div>${props.dangerousContent}</div>`}
/>enablePersistence
Type: boolean Default: false
Persist ListView state (scroll position, selection, checked items) in localStorage.
<ListViewComponent
enablePersistence={true}
/>
// State persisted to: localStorage['ComponentName']enableRtl
Type: boolean Default: false
Enable right-to-left layout for RTL languages.
<ListViewComponent enableRtl={true} />enableVirtualization
Type: boolean Default: false
Enable virtual scrolling for large datasets (1000+ items).
<ListViewComponent
dataSource={largeArray} // 5000+ items
enableVirtualization={true}
height="400px"
/>fields
Type: FieldSettingsModel Default: { id: 'id', text: 'text' }
Maps data source fields to ListView properties.
<ListViewComponent
fields={{
id: 'itemId', // Unique identifier
text: 'itemName', // Display text
child: 'subItems', // Nested items
groupBy: 'category', // Grouping field
image: 'imageUrl', // Image field
iconCss: 'iconClass', // Icon CSS class
sortBy: 'sortField', // Sort field
isChecked: 'checked', // Checkbox state
enabled: 'isEnabled' // Enable/disable state
}}
/>groupTemplate
Type: string | Function Default: null
Custom template for group headers when data is grouped.
const groupTemplate = (props: any) => (
<div style={{ fontWeight: 'bold', backgroundColor: '#f0f0f0', padding: '5px' }}>
{props.text} ({props.count} items)
</div>
);
<ListViewComponent
groupTemplate={groupTemplate}
fields={{ groupBy: 'category' }}
/>headerTemplate
Type: string | Function Default: null
Custom template for ListView header.
const headerTemplate = () => (
<div style={{ padding: '10px', backgroundColor: '#2196F3', color: 'white' }}>
<h3>My List</h3>
</div>
);
<ListViewComponent
headerTemplate={headerTemplate}
showHeader={true}
/>headerTitle
Type: string Default: ''
Title for the ListView header.
<ListViewComponent
headerTitle="Inbox"
showHeader={true}
/>height
Type: string | number Default: '100%'
Height of the ListView container.
// Pixel value
<ListViewComponent height="500px" />
// Percentage
<ListViewComponent height="100%" />
// Number (assumes px)
<ListViewComponent height={400} />htmlAttributes
Type: { [key: string]: string } Default: {}
Additional HTML attributes for the ListView container.
<ListViewComponent
htmlAttributes={{
'data-test': 'my-list',
'aria-label': 'Item list',
role: 'list'
}}
/>locale
Type: string Default: 'en-US'
Localization culture for the ListView.
<ListViewComponent locale="de-DE" />
// German localization
<ListViewComponent locale="ar-AE" />
// Arabic with RTL supportquery
Type: Query Default: null
DataManager query for filtering, sorting, and pagination.
import { Query } from '@syncfusion/ej2-data';
<ListViewComponent
dataSource={dataManager}
query={new Query()
.where('category', 'equal', 'Electronics')
.take(10)
.sortBy('price')
}
/>showCheckBox
Type: boolean Default: false
Show checkboxes for multi-select functionality.
<ListViewComponent showCheckBox={true} />showHeader
Type: boolean Default: false
Display the ListView header.
<ListViewComponent
showHeader={true}
headerTitle="My Items"
/>showIcon
Type: boolean Default: false
Show icons next to items (requires iconCss field).
<ListViewComponent
showIcon={true}
fields={{ iconCss: 'icon' }}
/>sortOrder
Type: 'Ascending' | 'Descending' | 'None' Default: 'None'
Sort order for list items.
// Sort ascending
<ListViewComponent sortOrder="Ascending" />
// Sort descending
<ListViewComponent sortOrder="Descending" />
// No sorting
<ListViewComponent sortOrder="None" />template
Type: string | Function Default: null
Custom template for list items.
// JSX template
const template = (props: any) => (
<div>
<strong>{props.text}</strong>
<p>{props.description}</p>
</div>
);
<ListViewComponent template={template} />width
Type: string | number Default: '100%'
Width of the ListView container.
// Pixel value
<ListViewComponent width="500px" />
// Percentage
<ListViewComponent width="100%" />
// Number (assumes px)
<ListViewComponent width={500} />Methods
addItem
Signature: addItem(data: any, fields?: FieldSettingsModel): void
Add single or multiple items to the ListView.
const listViewRef = useRef<ListViewComponent>(null);
const handleAddItem = () => {
// Add single item
listViewRef.current?.addItem({
id: '1',
text: 'New Item'
});
};
const handleAddMultiple = () => {
// Add multiple items
listViewRef.current?.addItem([
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
]);
};back
Signature: back(): void
Navigate back from nested ListView to parent.
const handleBackClick = () => {
listViewRef.current?.back?.();
};
<button onClick={handleBackClick}>← Back</button>checkAllItems
Signature: checkAllItems(): void
Check all checkboxes in the ListView.
const handleCheckAll = () => {
listViewRef.current?.checkAllItems?.();
};
<button onClick={handleCheckAll}>Check All</button>checkItem
Signature: checkItem(item: any): void
Check specific item checkbox.
const handleCheckItem = (itemId: string) => {
listViewRef.current?.checkItem?.({ id: itemId });
};destroy
Signature: destroy(): void
Destroy the ListView component and release all resources.
const handleDestroyListView = () => {
// Clean up the ListView before unmounting
listViewRef.current?.destroy?.();
};
// Typically used in useEffect cleanup
useEffect(() => {
return () => {
listViewRef.current?.destroy?.();
};
}, []);disableItem
Signature: disableItem(item: any): void
Disable an item.
const handleDisableItem = (itemId: string) => {
listViewRef.current?.disableItem?.({ id: itemId });
};enableItem
Signature: enableItem(item: any): void
Enable a disabled item.
const handleEnableItem = (itemId: string) => {
listViewRef.current?.enableItem?.({ id: itemId });
};findItem
Signature: findItem(fields: any): HTMLElement | null
Find item element by field values.
const handleFindItem = () => {
const element = listViewRef.current?.findItem?.({
id: '5',
text: 'Item 5'
});
if (element) {
console.log('Item found:', element);
}
};getSelectedItems
Signature: getSelectedItems(): SelectedCollection
Get all selected items.
const handleGetSelected = () => {
const selected = listViewRef.current?.getSelectedItems?.();
console.log('Selected items:', selected?.data);
console.log('Selected text:', selected?.text);
};hideItem
Signature: hideItem(item: any): void
Hide an item from display.
const handleHideItem = (itemId: string) => {
listViewRef.current?.hideItem?.({ id: itemId });
};refresh
Signature: refresh(): void
Refresh the ListView with current data.
const handleRefresh = () => {
listViewRef.current?.refresh?.();
};
<button onClick={handleRefresh}>Refresh</button>refreshItemHeight
Signature: refreshItemHeight(): void
Recalculate item heights for virtual scrolling.
const handleUpdateHeights = () => {
listViewRef.current?.refreshItemHeight?.();
};removeItem
Signature: removeItem(item: any): void
Remove item(s) from ListView.
const handleRemoveItem = (itemId: string) => {
// Remove single item
listViewRef.current?.removeItem?.({ id: itemId });
};
const handleRemoveMultiple = () => {
// Remove multiple items
listViewRef.current?.removeItem?.([
{ id: '1' },
{ id: '2' }
]);
};selectItem
Signature: selectItem(item: any): void
Select specific item.
const handleSelectItem = (itemId: string) => {
listViewRef.current?.selectItem?.({ id: itemId });
};selectMultipleItems
Signature: selectMultipleItems(items: any[]): void
Select multiple items at once.
const handleSelectMultiple = () => {
listViewRef.current?.selectMultipleItems?.([
{ id: '1' },
{ id: '2' },
{ id: '3' }
]);
};showItem
Signature: showItem(item: any): void
Show hidden item.
const handleShowItem = (itemId: string) => {
listViewRef.current?.showItem?.({ id: itemId });
};uncheckAllItems
Signature: uncheckAllItems(): void
Uncheck all checkboxes.
const handleUncheckAll = () => {
listViewRef.current?.uncheckAllItems?.();
};
<button onClick={handleUncheckAll}>Uncheck All</button>uncheckItem
Signature: uncheckItem(item: any): void
Uncheck specific item.
const handleUncheckItem = (itemId: string) => {
listViewRef.current?.uncheckItem?.({ id: itemId });
};unselectItem
Signature: unselectItem(item?: any): void
Deselect item.
const handleUnselectItem = (itemId: string) => {
listViewRef.current?.unselectItem?.({ id: itemId });
};removeMultipleItems
Signature: removeMultipleItems(items: any[]): void
Remove multiple items from the ListView at once.
const handleRemoveMultiple = () => {
listViewRef.current?.removeMultipleItems?.([
{ id: '1' },
{ id: '2' },
{ id: '3' }
]);
};Events
select
Type: (args: SelectEventArgs) => void
Fired when item is selected.
interface SelectEventArgs {
text: string; // Item text
data: any; // Item data
index: number; // Item index
isInteracted: boolean; // User interacted
eventType: string; // Event type
isCtrlKey: boolean; // Ctrl key pressed
isShiftKey: boolean; // Shift key pressed
isTouchEnd: boolean; // Touch event
element: HTMLElement; // DOM element
}
const handleSelect = (args: SelectEventArgs) => {
console.log('Selected:', args.text, args.data);
};
<ListViewComponent select={handleSelect} />actionBegin
Type: (args: ActionEventArgs) => void
Fired before action completes (before select, sort, filter, etc).
interface ActionEventArgs {
eventType: string; // 'select', 'sorting', 'filtering', 'drop'
cancel: boolean; // Cancel action
data?: any; // Action data
}
const handleActionBegin = (args: ActionEventArgs) => {
if (args.eventType === 'select') {
console.log('Before select');
}
};
<ListViewComponent actionBegin={handleActionBegin} />actionComplete
Type: (args: ActionEventArgs) => void
Fired after action completes.
const handleActionComplete = (args: ActionEventArgs) => {
if (args.eventType === 'select') {
console.log('After select');
}
if (args.eventType === 'drop') {
console.log('Item dropped');
}
};
<ListViewComponent actionComplete={handleActionComplete} />actionFailure
Type: (args: any) => void
Fired when action fails (e.g., data load error).
const handleActionFailure = (args: any) => {
console.error('Action failed:', args);
};
<ListViewComponent actionFailure={handleActionFailure} />scroll
Type: (args: ScrollEventArgs) => void
Fired when ListView is scrolled.
interface ScrollEventArgs {
isAtEnd: boolean; // Scrolled to end
isAtStart: boolean; // Scrolled to start
isInteracted: boolean; // User interacted
scrollTop: number; // Vertical scroll position
scrollLeft: number; // Horizontal scroll position
}
const handleScroll = (args: ScrollEventArgs) => {
if (args.isAtEnd) {
console.log('Load more items');
}
};
<ListViewComponent scroll={handleScroll} />Field Mapping
Complete FieldSettingsModel
interface FieldSettingsModel {
// Unique identifier field
id?: string;
// Display text field
text?: string;
// Nested/child items field
child?: string;
// Grouping field
groupBy?: string;
// Image URL field
image?: string;
// Icon CSS class field
iconCss?: string;
// Sort field (if different from text)
sortBy?: string;
// Checkbox state field
isChecked?: string;
// Enable/disable state field
enabled?: string;
}
// Usage example
<ListViewComponent
fields={{
id: 'itemId',
text: 'itemName',
child: 'subItems',
groupBy: 'itemCategory',
image: 'thumbnailUrl',
iconCss: 'iconClass',
sortBy: 'itemOrder',
isChecked: 'isSelected',
enabled: 'isActive'
}}
/>Type Definitions
AnimationSettings
interface AnimationSettings {
effect?: 'None' | 'FadeIn' | 'SlideUp' | 'SlideDown' | 'SlideLeft' | 'SlideRight' | 'Zoom' | 'Expand';
duration?: number;
easing?: 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear';
}SelectedCollection
interface SelectedCollection {
text: string[]; // Text of selected items
data: any[]; // Data of selected items
index: number[]; // Index of selected items
}
// Usage
const selected = listViewRef.current?.getSelectedItems?.();
console.log(selected?.text); // ['Item 1', 'Item 2']
console.log(selected?.data); // [{ id: 1, text: 'Item 1' }, ...]
console.log(selected?.index); // [0, 1]Query Filters
import { Query, Predicate } from '@syncfusion/ej2-data';
// Equals
new Query().where('field', 'equal', 'value')
// Not equal
new Query().where('field', 'notequal', 'value')
// Contains
new Query().where('field', 'contains', 'value')
// Greater than
new Query().where('field', 'greaterthan', 10)
// Less than
new Query().where('field', 'lessthan', 10)
// Starts with
new Query().where('field', 'startswith', 'prefix')
// Ends with
new Query().where('field', 'endswith', 'suffix')
// AND condition
new Query().where('field1', 'equal', 'value1')
.where('field2', 'equal', 'value2')
// OR condition
const predicate = new Predicate('field1', 'equal', 'value1')
.or('field2', 'equal', 'value2');
new Query().where(predicate)Data Binding & Rendering
Table of Contents
- Local Data Sources
- Object Data Mapping
- Remote Data with DataManager
- Query Filtering
- Pagination
- Dynamic Data Updates
Local Data Sources
String Array
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
const data = ['Apple', 'Banana', 'Orange', 'Mango'];
<ListViewComponent
id="string-list"
dataSource={data}
/>Number Array
const numbers = [1, 2, 3, 4, 5];
<ListViewComponent
id="number-list"
dataSource={numbers}
/>Simple Objects
const items = [
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' },
{ id: '3', text: 'Item 3' }
];
<ListViewComponent
id="object-list"
dataSource={items}
fields={{ id: 'id', text: 'text' }}
/>Object Data Mapping
Basic Field Mapping
interface Product {
productId: number;
productName: string;
category: string;
price: number;
}
const products: Product[] = [
{ productId: 1, productName: 'Laptop', category: 'Electronics', price: 999 },
{ productId: 2, productName: 'Desk', category: 'Furniture', price: 299 },
{ productId: 3, productName: 'Chair', category: 'Furniture', price: 199 }
];
<ListViewComponent
dataSource={products}
fields={{
id: 'productId',
text: 'productName',
tooltip: 'category'
}}
/>Field Mapping for Features
interface ComplexItem {
itemId: string;
itemName: string;
description: string;
isActive: boolean;
iconClass: string;
imageUrl: string;
childItems?: ComplexItem[];
showCheckBox?: boolean;
sortField?: string;
}
const data: ComplexItem[] = [
{
itemId: '1',
itemName: 'Documents',
description: 'My documents folder',
isActive: true,
iconClass: 'e-icons e-folder',
imageUrl: '/images/folder.png',
childItems: [
{ itemId: '1-1', itemName: 'Report.pdf', description: 'Q4 Report', isActive: true, iconClass: 'e-icons e-file', imageUrl: '', sortField: 'Report' }
],
sortField: 'Documents'
}
];
<ListViewComponent
dataSource={data}
fields={{
id: 'itemId',
text: 'itemName',
tooltip: 'description',
enabled: 'isActive',
iconCss: 'iconClass',
image: 'imageUrl',
child: 'childItems',
sortBy: 'sortField'
}}
/>Nested/Hierarchical Data
interface TreeItem {
id: string;
text: string;
child?: TreeItem[];
}
const hierarchyData: TreeItem[] = [
{
id: '1',
text: 'Asia',
child: [
{ id: '1.1', text: 'India', child: [
{ id: '1.1.1', text: 'Delhi' },
{ id: '1.1.2', text: 'Mumbai' }
]},
{ id: '1.2', text: 'China' }
]
},
{
id: '2',
text: 'Europe',
child: [
{ id: '2.1', text: 'Germany' },
{ id: '2.2', text: 'France' }
]
}
];
<ListViewComponent
dataSource={hierarchyData}
fields={{
id: 'id',
text: 'text',
child: 'child'
}}
/>Grouped Data
interface GroupedItem {
id: string;
text: string;
category: string;
}
const groupedData: GroupedItem[] = [
{ id: '1', text: 'Apple', category: 'Fruit' },
{ id: '2', text: 'Carrot', category: 'Vegetable' },
{ id: '3', text: 'Banana', category: 'Fruit' },
{ id: '4', text: 'Tomato', category: 'Vegetable' }
];
<ListViewComponent
dataSource={groupedData}
fields={{
id: 'id',
text: 'text',
groupBy: 'category' // ← Group by category field
}}
/>Remote Data with DataManager
Basic Remote Data
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
const data = new DataManager({
url: 'https://jsonplaceholder.typicode.com/users',
adaptor: new UrlAdaptor()
});
<ListViewComponent
dataSource={data}
fields={{
id: 'id',
text: 'name'
}}
headerTitle="Users"
showHeader={true}
/>With CORS Proxy
const data = new DataManager({
url: 'https://api.example.com/items',
adaptor: new UrlAdaptor(),
crossDomain: true
});Custom API Response Mapping
const data = new DataManager({
url: 'https://api.example.com/products',
adaptor: new UrlAdaptor()
});
<ListViewComponent
dataSource={data}
fields={{
id: 'productId',
text: 'productName',
tooltip: 'description'
}}
/>Error Handling
const handleActionFailure = (args: any) => {
console.error('Data fetch failed:', args);
alert('Failed to load data');
};
<ListViewComponent
dataSource={remoteData}
actionFailure={handleActionFailure}
/>Query Filtering
Basic Query
import { Query } from '@syncfusion/ej2-data';
const data = new DataManager({
url: 'https://jsonplaceholder.typicode.com/users'
});
const query = new Query()
.select(['id', 'name', 'email'])
.take(5);
<ListViewComponent
dataSource={data}
query={query}
fields={{ id: 'id', text: 'name' }}
/>Filter Results
// WHERE clause
const query = new Query()
.where('status', '==', 'active');
// Multiple conditions (AND)
const query = new Query()
.where('category', '==', 'Electronics')
.and('price', '<', 1000);
// OR condition
const query = new Query()
.where('status', '==', 'active')
.or('priority', '==', 'high');Search Filter Example
import { useState } from 'react';
import { Query } from '@syncfusion/ej2-data';
export function FilteredListView() {
const [searchText, setSearchText] = useState('');
const data = new DataManager({
url: 'https://api.example.com/items'
});
// Build dynamic query based on search
let query = new Query();
if (searchText) {
query = query.where('name', 'startswith', searchText);
}
return (
<div>
<input
type="text"
placeholder="Search items..."
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
style={{ marginBottom: '10px', padding: '8px' }}
/>
<ListViewComponent
dataSource={data}
query={query}
fields={{ id: 'id', text: 'name' }}
/>
</div>
);
}Sort & Order
const query = new Query()
.sortBy('name', 'ascending')
.take(10);
// Multiple sort
const query = new Query()
.sortBy('category', 'ascending')
.sortBy('name', 'ascending');Paging with Query
const query = new Query()
.skip(0) // Start from record 0
.take(10); // Get 10 records
// For page 2 with 10 items per page:
const page = 2;
const pageSize = 10;
const skipCount = (page - 1) * pageSize;
const query = new Query()
.skip(skipCount)
.take(pageSize);Pagination
Manual Pagination
import { useState } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
export function PaginatedListView() {
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10;
const totalItems = 100;
const allData = Array.from({ length: totalItems }, (_, i) => ({
id: (i + 1).toString(),
text: `Item ${i + 1}`
}));
// Calculate paginated data
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const paginatedData = allData.slice(startIndex, endIndex);
const totalPages = Math.ceil(totalItems / itemsPerPage);
return (
<div>
<ListViewComponent
dataSource={paginatedData}
fields={{ id: 'id', text: 'text' }}
height="400px"
/>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
<button
disabled={currentPage === 1}
onClick={() => setCurrentPage(currentPage - 1)}
>
Previous
</button>
<span style={{ margin: '0 10px' }}>
Page {currentPage} of {totalPages}
</span>
<button
disabled={currentPage === totalPages}
onClick={() => setCurrentPage(currentPage + 1)}
>
Next
</button>
</div>
</div>
);
}Load More Pattern
import { useState } from 'react';
export function LoadMoreListView() {
const [items, setItems] = useState(
Array.from({ length: 10 }, (_, i) => ({
id: (i + 1).toString(),
text: `Item ${i + 1}`
}))
);
const handleLoadMore = () => {
const nextItems = Array.from(
{ length: 10 },
(_, i) => ({
id: (items.length + i + 1).toString(),
text: `Item ${items.length + i + 1}`
})
);
setItems([...items, ...nextItems]);
};
return (
<div>
<ListViewComponent
dataSource={items}
fields={{ id: 'id', text: 'text' }}
height="400px"
/>
<button
onClick={handleLoadMore}
style={{ marginTop: '10px', width: '100%', padding: '10px' }}
>
Load More
</button>
</div>
);
}Dynamic Data Updates
Updating Entire DataSource
import { useState } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
export function DynamicListView() {
const [data, setData] = useState([
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
]);
const handleRefresh = () => {
// Fetch new data
const newData = [
{ id: '3', text: 'Item 3' },
{ id: '4', text: 'Item 4' },
{ id: '5', text: 'Item 5' }
];
setData(newData);
};
return (
<div>
<button onClick={handleRefresh} style={{ marginBottom: '10px' }}>
Refresh Data
</button>
<ListViewComponent
dataSource={data}
fields={{ id: 'id', text: 'text' }}
/>
</div>
);
}Adding Items Dynamically
const handleAddItem = () => {
const newData = [
...data,
{ id: Date.now().toString(), text: `New Item ${Date.now()}` }
];
setData(newData);
};Updating Specific Item
const handleUpdateItem = (itemId: string, newText: string) => {
const updatedData = data.map(item =>
item.id === itemId ? { ...item, text: newText } : item
);
setData(updatedData);
};Removing Items
const handleRemoveItem = (itemId: string) => {
const filteredData = data.filter(item => item.id !== itemId);
setData(filteredData);
};Complete CRUD Example
import { useState } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
interface ListItem {
id: string;
text: string;
}
export function CRUDListView() {
const [items, setItems] = useState<ListItem[]>([
{ id: '1', text: 'Task 1' },
{ id: '2', text: 'Task 2' }
]);
const [inputValue, setInputValue] = useState('');
// Create
const add = () => {
if (inputValue.trim()) {
setItems([
...items,
{ id: Date.now().toString(), text: inputValue }
]);
setInputValue('');
}
};
// Read (items are displayed)
// Update
const update = (id: string) => {
const newText = prompt('Enter new text:');
if (newText) {
setItems(items.map(item =>
item.id === id ? { ...item, text: newText } : item
));
}
};
// Delete
const remove = (id: string) => {
setItems(items.filter(item => item.id !== id));
};
return (
<div style={{ maxWidth: '400px' }}>
<div style={{ marginBottom: '10px' }}>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && add()}
placeholder="Add new item..."
style={{ width: '70%', padding: '5px' }}
/>
<button onClick={add} style={{ marginLeft: '5px' }}>
Add
</button>
</div>
<ListViewComponent
dataSource={items}
fields={{ id: 'id', text: 'text' }}
height="300px"
/>
<div style={{ marginTop: '10px' }}>
{items.map(item => (
<div key={item.id} style={{ marginBottom: '5px' }}>
<button onClick={() => update(item.id)} style={{ marginRight: '5px' }}>
Edit
</button>
<button onClick={() => remove(item.id)}>
Delete
</button>
</div>
))}
</div>
</div>
);
}Performance Tips
- Use
DataManagerwith remote data to avoid loading all items at once - Enable
enableVirtualizationfor large datasets - Use
Query().take(n)to limit initial data - Implement pagination for better UX with large datasets
- Avoid frequent re-renders by using
useMemofor field mappings
Getting Started with ListView
Table of Contents
Installation
Prerequisites
- React 16.8+ (for hooks support)
- Node.js 12+
- npm or yarn
Step 1: Install Required Packages
# Install ListView and dependencies
npm install @syncfusion/ej2-react-lists @syncfusion/ej2-base @syncfusion/ej2-data
# Or with yarn
yarn add @syncfusion/ej2-react-lists @syncfusion/ej2-base @syncfusion/ej2-dataStep 2: Verify Installation
# Check installed versions
npm list @syncfusion/ej2-react-lists
npm list @syncfusion/ej2-base
npm list @syncfusion/ej2-dataStep 3: License Key (Optional)
For production use, register your Syncfusion license key:
import { registerLicense } from '@syncfusion/ej2-base';
// Register once in your app entry point
registerLicense('YOUR_SYNCFUSION_LICENSE_KEY');CSS Themes
Available Built-in Themes
1. Material (Default)
import '@syncfusion/ej2-react-lists/styles/material.css';2. Bootstrap
import '@syncfusion/ej2-react-lists/styles/bootstrap.css';3. Fabric (Office)
import '@syncfusion/ej2-react-lists/styles/fabric.css';4. Tailwind
import '@syncfusion/ej2-react-lists/styles/tailwind.css';5. High Contrast
import '@syncfusion/ej2-react-lists/styles/highcontrast.css';Bootstrap Dark Theme
import '@syncfusion/ej2-react-lists/styles/bootstrap-dark.css';Applying a Theme
Choose ONE theme and import in your main component or App.tsx:
// App.tsx
import React from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import '@syncfusion/ej2-react-lists/styles/material.css'; // Choose one theme
function App() {
return (
<div>
<ListViewComponent id="list" dataSource={['Item 1', 'Item 2']} />
</div>
);
}
export default App;Basic Setup
Minimal Example
import React from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import '@syncfusion/ej2-react-lists/styles/material.css';
export function BasicList() {
// Simple string array
const items = ['Apple', 'Banana', 'Orange', 'Mango'];
return (
<ListViewComponent
id="simple-list"
dataSource={items}
/>
);
}With Object Data
import React from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import '@syncfusion/ej2-react-lists/styles/material.css';
export function ObjectList() {
const items = [
{ id: '1', name: 'Apple', category: 'Fruit' },
{ id: '2', name: 'Carrot', category: 'Vegetable' },
{ id: '3', name: 'Banana', category: 'Fruit' }
];
return (
<ListViewComponent
id="object-list"
dataSource={items}
fields={{
id: 'id',
text: 'name'
}}
/>
);
}First Component
Complete Working Example
import React, { useState, useRef } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import '@syncfusion/ej2-react-lists/styles/material.css';
export function FirstListView() {
const listViewRef = useRef<ListViewComponent>(null);
const [selectedItem, setSelectedItem] = useState<any>(null);
// Sample data
const data = [
{ id: '1', text: 'Inbox', icon: 'e-icons e-mail' },
{ id: '2', text: 'Sent', icon: 'e-icons e-send' },
{ id: '3', text: 'Drafts', icon: 'e-icons e-edit' },
{ id: '4', text: 'Trash', icon: 'e-icons e-delete' }
];
// Handle selection
const handleSelect = (args: any) => {
setSelectedItem(args);
console.log('Selected:', args.text);
};
// Add new item
const handleAddItem = () => {
const newItem = {
id: Date.now().toString(),
text: `New Item ${Date.now()}`,
icon: 'e-icons e-new'
};
if (listViewRef.current) {
listViewRef.current.addItem([newItem]);
}
};
return (
<div style={{ padding: '20px' }}>
<h2>Email Folders</h2>
<button onClick={handleAddItem} style={{ marginBottom: '10px' }}>
Add Folder
</button>
<ListViewComponent
ref={listViewRef}
id="email-list"
dataSource={data}
fields={{
id: 'id',
text: 'text',
iconCss: 'icon'
}}
select={handleSelect}
showIcon={true}
height="300px"
/>
{selectedItem && (
<div style={{ marginTop: '20px', padding: '10px', border: '1px solid #ccc' }}>
<p>Selected: <strong>{selectedItem.text}</strong></p>
<p>ID: {selectedItem.id}</p>
</div>
)}
</div>
);
}TypeScript Support
Type Definitions
ListView includes TypeScript type definitions. No additional setup needed.
import React, { useRef } from 'react';
import { ListViewComponent, ListViewComponent as ListView } from '@syncfusion/ej2-react-lists';
import { SelectEventArgs, SelectedItem } from '@syncfusion/ej2-react-lists';
interface DataItem {
id: string;
text: string;
category?: string;
icon?: string;
}
export function TypedListView() {
const listViewRef = useRef<ListView>(null);
const data: DataItem[] = [
{ id: '1', text: 'Item 1', category: 'Category A' },
{ id: '2', text: 'Item 2', category: 'Category B' }
];
const handleSelect = (args: SelectEventArgs): void => {
console.log('Selected:', args.text);
};
return (
<ListViewComponent
ref={listViewRef}
dataSource={data}
fields={{ id: 'id', text: 'text', groupBy: 'category' }}
select={handleSelect}
/>
);
}Common Type Definitions
// Data item fields
interface FieldSettings {
id?: string; // Item identifier
text?: string; // Display text
tooltip?: string; // Hover tooltip
child?: string; // Nested items
icon?: string; // Icon CSS class
image?: string; // Image URL
isChecked?: string; // Checkbox state field
isVisible?: string; // Visibility field
enabled?: string; // Enabled state field
groupBy?: string; // Grouping field
}
// Select event args
interface SelectEventArgs {
id: string;
text: string;
element?: HTMLElement;
data?: any;
isChecked?: boolean;
index?: number;
}
// Selected items
interface SelectedItem {
id: string;
text: string;
element: HTMLElement;
data: any;
}Project Structure
Recommended Project Layout
my-react-app/
├── src/
│ ├── components/
│ │ └── ListView/
│ │ ├── BasicList.tsx
│ │ ├── ListWithSelection.tsx
│ │ ├── ListWithTemplates.tsx
│ │ └── ListWithFiltering.tsx
│ ├── styles/
│ │ └── listview.css
│ ├── App.tsx
│ ├── App.css
│ └── index.tsx
├── package.json
└── tsconfig.jsonApp.tsx Setup
import React from 'react';
import '@syncfusion/ej2-react-lists/styles/material.css';
import './App.css';
import { BasicList } from './components/ListView/BasicList';
function App() {
return (
<div className="app-container">
<header>
<h1>ListView Examples</h1>
</header>
<main>
<BasicList />
</main>
</div>
);
}
export default App;Custom CSS
/* src/styles/listview.css */
.my-list-container {
max-width: 500px;
margin: 20px auto;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.e-listview .e-list-item {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
}
.e-listview .e-list-item:hover {
background-color: #f5f5f5;
}
.e-listview .e-list-item.e-active {
background-color: #e3f2fd;
border-left: 4px solid #2196f3;
}Common Issues & Solutions
Issue: Component Not Rendering
Solution: Ensure CSS is imported BEFORE using the component
// ✅ Correct order
import '@syncfusion/ej2-react-lists/styles/material.css';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
// ❌ Wrong order
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import '@syncfusion/ej2-react-lists/styles/material.css';Issue: TypeScript Errors with Ref
Solution: Use proper typing
// ✅ Correct
const listViewRef = useRef<ListViewComponent>(null);
// ❌ Wrong
const listViewRef = useRef(null);Issue: Data Not Displaying
Solution: Check fields mapping
// If your data has these fields:
const data = [
{ productId: 1, productName: 'Item 1' }
];
// Map them correctly:
<ListViewComponent
dataSource={data}
fields={{
id: 'productId', // ← Map to your field names
text: 'productName' // ← Not the default 'id' or 'text'
}}
/>Next Steps
- Selection: Read selection-filtering.md for handling item selection
- Data Binding: Read data-binding-rendering.md for advanced data sources
- Templates: Read templating-customization.md for custom item displays
- Advanced: Read advanced-features.md for drag-drop, grouping, etc.
Item Management (CRUD Operations)
Table of Contents
Adding Items
Add Single Item
import { useRef } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
export function AddItemExample() {
const listViewRef = useRef<ListViewComponent>(null);
const data = [
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
];
const handleAddItem = () => {
const newItem = {
id: Date.now().toString(),
text: `New Item ${Date.now()}`
};
if (listViewRef.current) {
listViewRef.current.addItem([newItem]);
}
};
return (
<div>
<button onClick={handleAddItem}>Add Item</button>
<ListViewComponent
ref={listViewRef}
dataSource={data}
fields={{ id: 'id', text: 'text' }}
/>
</div>
);
}Add with Object Properties
interface Product {
id: string;
name: string;
price: number;
category: string;
}
const handleAddProduct = () => {
const newProduct: Product = {
id: `prod-${Date.now()}`,
name: 'New Product',
price: 99.99,
category: 'Electronics'
};
listViewRef.current.addItem([newProduct]);
};Add Item at Specific Position
// ListView doesn't support "addAt" position, but you can:
// 1. Manipulate state and update dataSource
// 2. Use removeItem to remove, then addItem to insert
const handleInsertAtPosition = (index: number) => {
const allData = [...data];
const newItem = { id: `id-${Date.now()}`, text: 'Inserted Item' };
// Insert at position
allData.splice(index, 0, newItem);
// Update state (requires managed state)
setData(allData);
};Add Multiple Items
const handleAddMultipleItems = () => {
const newItems = [
{ id: '10', text: 'Item 10' },
{ id: '11', text: 'Item 11' },
{ id: '12', text: 'Item 12' }
];
if (listViewRef.current) {
listViewRef.current.addItem(newItems); // Pass array
}
};Validate Before Adding
const handleAddValidated = (itemText: string) => {
// Validation
if (!itemText || itemText.trim() === '') {
alert('Item text cannot be empty');
return;
}
if (itemText.length > 100) {
alert('Item text too long');
return;
}
// Check for duplicates
const isDuplicate = data.some(item => item.text === itemText);
if (isDuplicate) {
alert('Item already exists');
return;
}
// Add item
const newItem = {
id: Date.now().toString(),
text: itemText
};
listViewRef.current.addItem([newItem]);
};Removing Items
Remove by Reference
const handleRemoveItem = () => {
// Get the selected item
const selected = listViewRef.current.getSelectedItems();
if (selected) {
listViewRef.current.removeItem(selected.element);
}
};Remove by Element
const handleRemoveByElement = (itemId: string) => {
// Get element by id
const element = document.getElementById(itemId);
if (element) {
listViewRef.current.removeItem(element);
}
};Remove by Fields
const handleRemoveByFields = (itemId: string, itemText: string) => {
const itemFields = {
id: itemId,
text: itemText
};
listViewRef.current.removeItem(itemFields);
};Remove Multiple Items
const handleRemoveMultiple = () => {
const itemsToRemove = [
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
];
itemsToRemove.forEach(item => {
listViewRef.current.removeItem(item);
});
};Remove All Items
const handleRemoveAllItems = () => {
if (confirm('Remove all items?')) {
data.forEach(item => {
listViewRef.current.removeItem(item);
});
}
};Remove with Confirmation
const handleRemoveWithConfirm = () => {
const selected = listViewRef.current.getSelectedItems();
if (selected && confirm(`Remove "${selected.text}"?`)) {
listViewRef.current.removeItem(selected.element);
} else if (!selected) {
alert('No item selected');
}
};Finding Items
Find Item by Reference
const handleFindItem = (itemId: string) => {
const itemFields = { id: itemId };
const foundItem = listViewRef.current.findItem(itemFields);
console.log('Found:', foundItem);
// Returns: { id, text, element, data }
};Find in Current Data
const handleSearchInData = (searchText: string) => {
const found = data.find(item => item.text === searchText);
if (found) {
console.log('Found item:', found);
} else {
console.log('Item not found');
}
};Find Multiple Matching Items
const handleSearchMultiple = (searchText: string) => {
const matches = data.filter(item =>
item.text.toLowerCase().includes(searchText.toLowerCase())
);
console.log('Matching items:', matches);
};Get Item Index
const handleGetItemIndex = (itemId: string) => {
const index = data.findIndex(item => item.id === itemId);
if (index !== -1) {
console.log(`Item at index: ${index}`);
} else {
console.log('Item not found');
}
};Updating Items
Update Item Text
import { useState } from 'react';
export function UpdateItemExample() {
const [data, setData] = useState([
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
]);
const handleUpdateItem = (itemId: string, newText: string) => {
const updated = data.map(item =>
item.id === itemId ? { ...item, text: newText } : item
);
setData(updated);
};
return (
<div>
<button onClick={() => handleUpdateItem('1', 'Updated Item 1')}>
Update Item 1
</button>
<ListViewComponent dataSource={data} />
</div>
);
}Update with Dialog
const handleUpdateWithDialog = (itemId: string) => {
const oldItem = data.find(item => item.id === itemId);
if (!oldItem) return;
const newText = prompt('Edit item:', oldItem.text);
if (newText !== null && newText.trim() !== '') {
const updated = data.map(item =>
item.id === itemId ? { ...item, text: newText } : item
);
setData(updated);
}
};Update Multiple Fields
interface Item {
id: string;
text: string;
category?: string;
isActive?: boolean;
}
const handleUpdateMultipleFields = (itemId: string, updates: Partial<Item>) => {
const updated: Item[] = data.map(item =>
item.id === itemId ? { ...item, ...updates } : item
);
setData(updated);
};
// Usage:
handleUpdateMultipleFields('1', {
text: 'Updated Text',
category: 'NewCategory',
isActive: false
});Refresh After Update
const handleUpdateAndRefresh = (itemId: string, newText: string) => {
// Update the item
const updated = data.map(item =>
item.id === itemId ? { ...item, text: newText } : item
);
setData(updated);
// Refresh ListView if needed
listViewRef.current?.refresh?.();
};Batch Operations
Add Multiple & Remove Multiple
const handleBatchUpdate = async () => {
// Remove old items
const itemsToRemove = [
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
];
itemsToRemove.forEach(item => {
listViewRef.current.removeItem(item);
});
// Add new items
const newItems = [
{ id: '10', text: 'New Item 1' },
{ id: '11', text: 'New Item 2' }
];
listViewRef.current.addItem(newItems);
};Bulk Update
const handleBulkUpdate = (updates: Record<string, string>) => {
const updated = data.map(item =>
updates[item.id] ? { ...item, text: updates[item.id] } : item
);
setData(updated);
};
// Usage:
handleBulkUpdate({
'1': 'Updated Item 1',
'2': 'Updated Item 2',
'3': 'Updated Item 3'
});Clear & Reload
const handleClearAndReload = async () => {
// Get all current items
const allItems = data;
// Remove each item
allItems.forEach(item => {
listViewRef.current.removeItem(item);
});
// Fetch new data and add
const newData = await fetchItemsFromAPI();
listViewRef.current.addItem(newData);
};Managing Nested Items
Add Nested Item
interface NestedItem {
id: string;
text: string;
child?: NestedItem[];
}
const handleAddNestedItem = (parentId: string) => {
const newNestedItem: NestedItem = {
id: `nested-${Date.now()}`,
text: 'Nested Item'
};
// Find parent
const parent = findItemRecursive(data, parentId);
if (parent) {
if (!parent.child) {
parent.child = [];
}
parent.child.push(newNestedItem);
setData([...data]); // Trigger re-render
}
};
const findItemRecursive = (items: NestedItem[], id: string): NestedItem | null => {
for (const item of items) {
if (item.id === id) return item;
if (item.child) {
const found = findItemRecursive(item.child, id);
if (found) return found;
}
}
return null;
};Remove Nested Item
const handleRemoveNestedItem = (parentId: string, childId: string) => {
const parent = findItemRecursive(data, parentId);
if (parent && parent.child) {
parent.child = parent.child.filter(child => child.id !== childId);
setData([...data]);
}
};Complete Item Management Example
import { useState, useRef } from 'react';
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
interface Item {
id: string;
text: string;
}
export function ItemManagementComplete() {
const [items, setItems] = useState<Item[]>([
{ id: '1', text: 'Item 1' },
{ id: '2', text: 'Item 2' }
]);
const [inputValue, setInputValue] = useState('');
const listViewRef = useRef<ListViewComponent>(null);
// Create
const addItem = () => {
if (inputValue.trim()) {
const newItem: Item = {
id: Date.now().toString(),
text: inputValue
};
setItems([...items, newItem]);
setInputValue('');
}
};
// Update
const updateItem = (id: string) => {
const newText = prompt('Edit item:');
if (newText) {
setItems(items.map(item =>
item.id === id ? { ...item, text: newText } : item
));
}
};
// Delete
const deleteItem = (id: string) => {
setItems(items.filter(item => item.id !== id));
};
// Find
const searchItem = (searchText: string) => {
const found = items.filter(item =>
item.text.toLowerCase().includes(searchText.toLowerCase())
);
console.log('Found:', found);
};
return (
<div style={{ maxWidth: '500px' }}>
<div style={{ marginBottom: '15px' }}>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addItem()}
placeholder="Enter new item..."
style={{ padding: '8px', width: '70%' }}
/>
<button onClick={addItem} style={{ marginLeft: '5px' }}>
Add
</button>
</div>
<ListViewComponent
ref={listViewRef}
dataSource={items}
fields={{ id: 'id', text: 'text' }}
height="300px"
/>
<div style={{ marginTop: '15px' }}>
{items.map(item => (
<div key={item.id} style={{ marginBottom: '8px' }}>
<span style={{ marginRight: '10px' }}>{item.text}</span>
<button onClick={() => updateItem(item.id)} style={{ marginRight: '5px' }}>
Edit
</button>
<button onClick={() => deleteItem(item.id)}>
Delete
</button>
</div>
))}
</div>
</div>
);
}````markdown
Layout & Alignment Patterns for Complex UI
This guide covers alignment fixes and best practices for ListView when used in complex layouts like patient portals, appointment dashboards, and monitoring dashboards.
Table of Contents
- Common Alignment Issues
- Patient Portal Pattern
- Appointments Dashboard Pattern
- Monitoring Dashboard Pattern
- Responsive Grid Layout
- Troubleshooting Alignment
Common Alignment Issues
Issue 1: ListView Not Filling Container
Problem: ListView appears small or doesn't fill available space
Solution:
// ❌ Wrong - ListView doesn't fill
<div style={{ border: '1px solid #e0e0e0' }}>
<ListViewComponent dataSource={data} />
</div>
// ✅ Correct - ListView fills container
<div style={{
border: '1px solid #e0e0e0',
display: 'flex',
flexDirection: 'column',
height: '400px' // Set explicit height
}}>
<ListViewComponent
dataSource={data}
height="100%" // Fill height
width="100%" // Fill width
/>
</div>Issue 2: Items Not Aligned in Template
Problem: Items have inconsistent padding/margins
Solution:
// ❌ Wrong - Inconsistent margins
const template = (props) => (
<div>
<span style={{ marginRight: '10px' }}>Icon</span>
<span style={{ marginLeft: '5px' }}>Text</span>
</div>
);
// ✅ Correct - Use gap for consistent spacing
const template = (props) => (
<div style={{
display: 'flex',
alignItems: 'center',
padding: '12px 16px',
gap: '12px', // Consistent spacing
boxSizing: 'border-box'
}}>
<span>Icon</span>
<span>Text</span>
</div>
);Issue 3: Scrolling Not Working in Card
Problem: ListView content doesn't scroll inside card container
Solution:
// ❌ Wrong - No scrolling
<div style={{ border: '1px solid #e0e0e0' }}>
<div>Header</div>
<ListViewComponent dataSource={data} height="300px" />
</div>
// ✅ Correct - Proper scrolling
<div style={{
border: '1px solid #e0e0e0',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{ flexShrink: 0 }}>Header</div> {/* No shrink */}
<div style={{
flex: 1,
overflow: 'auto', {/* Enable scrolling */}
minHeight: 0 {/* Allow shrinking below content size */}
}}>
<ListViewComponent dataSource={data} height="100%" />
</div>
</div>Issue 4: Border Misalignment
Problem: ListView borders don't align with card borders
Solution:
// ✅ Correct - Nested borders alignment
const cardStyle = {
border: '1px solid #e0e0e0',
borderRadius: '4px',
overflow: 'hidden' // Important: clips ListV iew to border-radius
};
const cardHeaderStyle = {
padding: '16px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #e0e0e0'
};
<div style={cardStyle}>
<div style={cardHeaderStyle}>Title</div>
<ListViewComponent dataSource={data} />
</div>Patient Portal Pattern
Complete patient portal with three sections: Appointments, Prescriptions, Messages.
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Page } from '@syncfusion/ej2-react-grids';
interface Appointment {
id: string;
date: string;
doctor: string;
time: string;
type: string;
}
interface Prescription {
id: string;
medication: string;
dosage: string;
prescribedBy: string;
refills: number;
}
interface Message {
id: string;
senderName: string;
subject: string;
date: string;
isRead: boolean;
}
export function PatientPortal() {
const appointmentsData: Appointment[] = [
{
id: '1',
date: '2026-06-15',
doctor: 'Dr. Michael Chen',
time: '10:00 AM',
type: 'Cardiology Consultation'
},
{
id: '2',
date: '2026-06-20',
doctor: 'Dr. Sarah Johnson',
time: '2:30 PM',
type: 'General Checkup'
}
];
const prescriptionsData: Prescription[] = [
{
id: '1',
medication: 'Lisinopril 10mg',
dosage: '1 tablet daily',
prescribedBy: 'Dr. Sarah Johnson',
refills: 3
},
{
id: '2',
medication: 'Metformin 500mg',
dosage: '2 tablets twice daily',
prescribedBy: 'Dr. Emily Martinez',
refills: 5
},
{
id: '3',
medication: 'Atorvastatin 20mg',
dosage: '1 tablet at bedtime',
prescribedBy: 'Dr. Sarah Johnson',
refills: 1
}
];
const messagesData: Message[] = [
{
id: '1',
senderName: 'Dr. Sarah Johnson',
subject: 'Lab Results Review',
date: '2026-05-29',
isRead: true
},
{
id: '2',
senderName: 'Dr. Michael Chen',
subject: 'Appointment Reminder',
date: '2026-05-28',
isRead: false
},
{
id: '3',
senderName: 'Billing Department',
subject: 'Payment Confirmation',
date: '2026-05-27',
isRead: true
}
];
const appointmentTemplate = (props: Appointment) => (
<div style={{
display: 'flex',
flexDirection: 'column',
padding: '12px 16px',
borderBottom: '1px solid #f0f0f0',
gap: '4px'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start' }}>
<div>
<div style={{ fontWeight: 600 }}>{props.doctor}</div>
<div style={{ fontSize: '12px', color: '#666' }}>{props.type}</div>
</div>
<div style={{ textAlign: 'right' }}>
<div style={{ fontSize: '12px', color: '#999' }}>{props.date}</div>
<div style={{ fontSize: '12px', color: '#999' }}>{props.time}</div>
</div>
</div>
</div>
);
const messageTemplate = (props: Message) => (
<div style={{
display: 'flex',
flexDirection: 'column',
padding: '12px 16px',
borderBottom: '1px solid #f0f0f0',
backgroundColor: props.isRead ? 'white' : '#e3f2fd',
borderLeft: `4px solid ${props.isRead ? 'transparent' : '#2196F3'}`,
gap: '4px'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontWeight: 600, color: '#333' }}>{props.senderName}</span>
<span style={{ fontSize: '12px', color: '#999' }}>{props.date}</span>
</div>
<div style={{ fontSize: '13px', color: '#666' }}>{props.subject}</div>
</div>
);
const cardContainerStyle = {
border: '1px solid #e0e0e0',
borderRadius: '4px',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column' as const
};
const cardHeaderStyle = {
padding: '16px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #e0e0e0',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
};
const cardBodyStyle = {
flex: 1,
overflow: 'auto',
minHeight: 0
};
const sectionContainerStyle = {
display: 'grid',
gridTemplateColumns: '2fr 1fr',
gap: '16px',
padding: '16px'
};
const rightSidebarStyle = {
display: 'flex',
flexDirection: 'column' as const,
gap: '16px'
};
return (
<div>
{/* Top Navigation */}
<div style={{
backgroundColor: '#f5f5f5',
padding: '16px',
borderBottom: '1px solid #e0e0e0',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<div>
<h1 style={{ margin: 0 }}>Patient Portal</h1>
<p style={{ margin: '4px 0 0 0', color: '#666' }}>Welcome back, John Doe</p>
</div>
<div style={{ display: 'flex', gap: '16px' }}>
<button>Appointments</button>
<button>Records</button>
<button>Billing</button>
<button style={{ position: 'relative' }}>
Messages
<span style={{
position: 'absolute',
top: '-8px',
right: '-8px',
backgroundColor: '#f44336',
color: 'white',
borderRadius: '50%',
width: '20px',
height: '20px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '12px'
}}>3</span>
</button>
</div>
</div>
{/* Main Content */}
<div style={sectionContainerStyle}>
{/* Left Column - Appointments & Prescriptions */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
{/* Appointments */}
<div style={cardContainerStyle}>
<div style={{
...cardHeaderStyle,
justifyContent: 'space-between'
}}>
<h3 style={{ margin: 0 }}>Upcoming Appointments</h3>
<button style={{
backgroundColor: '#2196F3',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '3px',
cursor: 'pointer'
}}>
Book New
</button>
</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={appointmentsData}
fields={{ id: 'id', text: 'doctor' }}
template={appointmentTemplate}
height="300px"
width="100%"
/>
</div>
</div>
{/* Prescriptions */}
<div style={cardContainerStyle}>
<div style={{
...cardHeaderStyle,
justifyContent: 'space-between'
}}>
<h3 style={{ margin: 0 }}>Current Prescriptions</h3>
<button style={{
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '3px',
cursor: 'pointer'
}}>
Request Refill
</button>
</div>
<div style={cardBodyStyle}>
<table style={{
width: '100%',
borderCollapse: 'collapse' as const
}}>
<thead style={{ backgroundColor: '#f5f5f5' }}>
<tr>
<th style={{ padding: '12px', textAlign: 'left', borderBottom: '1px solid #e0e0e0' }}>Medication</th>
<th style={{ padding: '12px', textAlign: 'left', borderBottom: '1px solid #e0e0e0' }}>Dosage</th>
<th style={{ padding: '12px', textAlign: 'left', borderBottom: '1px solid #e0e0e0' }}>Prescribed By</th>
<th style={{ padding: '12px', textAlign: 'left', borderBottom: '1px solid #e0e0e0' }}>Refills</th>
</tr>
</thead>
<tbody>
{prescriptionsData.map(rx => (
<tr key={rx.id}>
<td style={{ padding: '12px', borderBottom: '1px solid #f0f0f0' }}>{rx.medication}</td>
<td style={{ padding: '12px', borderBottom: '1px solid #f0f0f0' }}>{rx.dosage}</td>
<td style={{ padding: '12px', borderBottom: '1px solid #f0f0f0' }}>{rx.prescribedBy}</td>
<td style={{ padding: '12px', borderBottom: '1px solid #f0f0f0' }}>{rx.refills}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
{/* Right Column - Messages & Lab Results */}
<div style={rightSidebarStyle}>
{/* Messages */}
<div style={cardContainerStyle}>
<div style={{
...cardHeaderStyle,
justifyContent: 'space-between'
}}>
<h3 style={{ margin: 0 }}>Message Your Provider</h3>
<span style={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: '24px',
height: '24px',
backgroundColor: '#f44336',
color: 'white',
borderRadius: '50%',
fontSize: '12px',
fontWeight: 'bold'
}}>
3
</span>
</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={messagesData}
fields={{ id: 'id', text: 'subject' }}
template={messageTemplate}
height="350px"
width="100%"
/>
</div>
</div>
{/* Lab Results */}
<div style={cardContainerStyle}>
<div style={{
...cardHeaderStyle,
justifyContent: 'space-between'
}}>
<h3 style={{ margin: 0 }}>Recent Lab Results</h3>
<button style={{
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '3px',
cursor: 'pointer',
fontSize: '12px'
}}>
View All
</button>
</div>
<div style={{ padding: '16px' }}>
{/* Lab results table */}
</div>
</div>
</div>
</div>
</div>
);
}Appointments Dashboard Pattern
Responsive 3x2 grid layout with filters, scheduler, and analytics charts.
export function AppointmentsDashboard() {
const dashboardContainerStyle = {
display: 'grid',
gridTemplateColumns: '250px 1fr',
gap: '16px',
padding: '16px',
minHeight: '100vh'
};
const sidebarStyle = {
display: 'flex',
flexDirection: 'column' as const,
gap: '16px'
};
const contentAreaStyle = {
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: '16px'
};
const cardContainerStyle = {
border: '1px solid #e0e0e0',
borderRadius: '4px',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column' as const
};
const cardHeaderStyle = {
padding: '12px 16px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #e0e0e0',
fontWeight: 600
};
const cardBodyStyle = {
flex: 1,
overflow: 'auto',
minHeight: 0,
padding: '16px'
};
return (
<div style={dashboardContainerStyle}>
{/* Left Sidebar Navigation */}
<div style={sidebarStyle}>
<div style={{
...cardContainerStyle,
height: 'fit-content'
}}>
<div style={cardHeaderStyle}>Dashboard</div>
<ListViewComponent
dataSource={[
{ id: '1', text: 'Dashboard' },
{ id: '2', text: 'Monitoring' },
{ id: '3', text: 'Users' },
{ id: '4', text: 'Analytics' },
{ id: '5', text: 'Reports' },
{ id: '6', text: 'Settings' }
]}
height="auto"
/>
</div>
</div>
{/* Main Content Area */}
<div style={contentAreaStyle}>
{/* Row 1 - Filters & Today's Appointments */}
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Filter by Doctor</div>
<div style={cardBodyStyle}>
{/* Dropdown component */}
</div>
</div>
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Today's Appointments</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={appointmentsData}
height="250px"
width="100%"
/>
</div>
</div>
{/* Row 2 - Calendar (spans 2 columns) */}
<div style={{
...cardContainerStyle,
gridColumn: 'span 2'
}}>
<div style={cardHeaderStyle}>Appointment Calendar</div>
<div style={cardBodyStyle}>
{/* Scheduler component */}
</div>
</div>
{/* Row 3 - Charts */}
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Doctor Occupancy</div>
<div style={cardBodyStyle}>
{/* Bar chart */}
</div>
</div>
<div style={cardContainerStyle}>
<div style={cardHeaderStyle}>Appointment Status</div>
<div style={cardBodyStyle}>
{/* Pie chart */}
</div>
</div>
</div>
</div>
);
}Monitoring Dashboard Pattern
System monitoring dashboard with 3x3 grid layout.
export function MonitoringDashboard() {
const mainGridStyle = {
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '16px',
padding: '16px'
};
const cardStyle = {
border: '1px solid #e0e0e0',
borderRadius: '4px',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column' as const,
minHeight: '300px'
};
const cardHeaderStyle = {
padding: '12px 16px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #e0e0e0',
fontWeight: 600,
flexShrink: 0
};
const cardBodyStyle = {
flex: 1,
overflow: 'auto',
minHeight: 0,
padding: '16px'
};
return (
<div style={mainGridStyle}>
{/* Top Row - KPIs */}
<div style={cardStyle}>
<div style={cardHeaderStyle}>CPU Usage</div>
<div style={cardBodyStyle}>
<div style={{ fontSize: '32px', fontWeight: 'bold', color: '#2196F3' }}>45%</div>
</div>
</div>
<div style={cardStyle}>
<div style={cardHeaderStyle}>Memory Usage</div>
<div style={cardBodyStyle}>
<div style={{ fontSize: '32px', fontWeight: 'bold', color: '#ff9800' }}>72%</div>
</div>
</div>
<div style={cardStyle}>
<div style={cardHeaderStyle}>Error Count</div>
<div style={cardBodyStyle}>
<div style={{ fontSize: '32px', fontWeight: 'bold', color: '#f44336' }}>23</div>
</div>
</div>
{/* Middle Row */}
<div style={cardStyle}>
<div style={cardHeaderStyle}>Log Stream</div>
<div style={cardBodyStyle}>
{/* Log stream list */}
</div>
</div>
<div style={cardStyle}>
<div style={cardHeaderStyle}>API Response Times</div>
<div style={cardBodyStyle}>
{/* Line chart */}
</div>
</div>
<div style={cardStyle}>
<div style={cardHeaderStyle}>Requests per Service</div>
<div style={cardBodyStyle}>
{/* Bar chart */}
</div>
</div>
{/* Bottom Row */}
<div style={cardStyle}>
<div style={cardHeaderStyle}>Deployment History</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={deploymentData}
height="100%"
width="100%"
/>
</div>
</div>
<div style={cardStyle}>
<div style={cardHeaderStyle}>Active Alerts</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={alertsData}
template={alertTemplate}
height="100%"
width="100%"
/>
</div>
</div>
<div style={cardStyle}>
<div style={cardHeaderStyle}>Open Tickets</div>
<div style={cardBodyStyle}>
<ListViewComponent
dataSource={ticketsData}
template={ticketTemplate}
height="100%"
width="100%"
/>
</div>
</div>
</div>
);
}Responsive Grid Layout
Make ListView grid layouts responsive for mobile, tablet, and desktop.
import { useState, useEffect } from 'react';
export function ResponsiveGridLayout() {
const [gridColumns, setGridColumns] = useState('repeat(3, 1fr)');
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
if (width < 768) {
setGridColumns('1fr'); // Mobile: 1 column
} else if (width < 1024) {
setGridColumns('repeat(2, 1fr)'); // Tablet: 2 columns
} else {
setGridColumns('repeat(3, 1fr)'); // Desktop: 3 columns
}
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const gridStyle = {
display: 'grid',
gridTemplateColumns: gridColumns,
gap: '16px',
padding: '16px',
transition: 'grid-template-columns 0.3s ease'
};
return (
<div style={gridStyle}>
{/* Cards */}
</div>
);
}Troubleshooting Alignment
| Issue | Cause | Solution |
|---|---|---|
| ListView cuts off at bottom | Missing minHeight: 0 on parent | Add minHeight: 0 to flex container |
| Items not vertically centered | Missing alignItems: 'center' | Add to flex container in template |
| Horizontal scrollbar appears | Width not set correctly | Add width: '100%' to ListView |
| Header always scrolls | flexShrink: 0 not set | Add flexShrink: 0 to header |
| Card borders misaligned | Default ListView borders | Use overflow: 'hidden' on card |
| Items have huge gaps | Margin stacking | Use gap instead of margin |
| Content overflow hidden | Parent height too small | Increase parent height or use overflow: auto |
| ListViews overlap in grid | Wrong grid span | Check gridColumn and gridRow |
````