
Syncfusion React Dashboard Layout
- 435 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
Use syncfusion-react-dashboard-layout for development tasks
About
syncfusion-react-dashboard-layout: A skill for development. This provides functionality for development workflows.
- syncfusion-react-dashboard-layout
Syncfusion React Dashboard Layout by the numbers
- 435 all-time installs (skills.sh)
- +60 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,008 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-dashboard-layoutAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 435 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
What it does
Use syncfusion-react-dashboard-layout for development tasks
Files
Implementing Syncfusion React Dashboard Layout
Syncfusion React Dashboard Layout is a powerful component for building responsive, interactive dashboard interfaces with draggable and resizable panels. It provides a flexible grid-based system for organizing content, automatic floating arrangement, and comprehensive state management.
Key Capabilities:
- Multi-column grid layouts with flexible cell sizing
- Drag-and-drop panel repositioning
- Resizable panels with size constraints
- Automatic floating arrangement (fills empty spaces)
- Responsive design with media queries
- Dynamic panel add/remove/update operations
- State persistence across sessions
- Comprehensive event system for monitoring changes
- Accessibility support and RTL rendering
Documentation and Navigation Guide
Choose the reference that matches your current task:
🚀 Getting Started
📄 Read: references/getting-started.md
- Installation and package setup
- CSS theme imports and configuration
- Creating your first dashboard
- Basic panel implementation
- Common setup issues and solutions
🎯 Core Concepts
📄 Read: references/core-functionality.md
- Panel creation and configuration (PanelModel interface)
- Basic dragging and positioning
- Basic resizing with constraints
- Grid configuration fundamentals
- Complete component overview
📄 Read: references/properties-reference.md
- All 15 component properties documented
- Layout properties (columns, cellSpacing, cellAspectRatio)
- Customization options (enableRtl, enablePersistence, showGridLines)
- Advanced properties (mediaQuery, draggableHandle, resizableHandles)
- Complete property reference with patterns
🎨 User Interface & Customization
📄 Read: references/panel-templates.md
- Panel structure and composition
- Header templates (text, HTML, interactive)
- Content templates (HTML strings, JSX, Syncfusion components)
- Embedding charts, grids, and widgets
- Dynamic content updates
- Template best practices and optimization
📄 Read: references/styling-customization.md
- Complete CSS selector reference
- Theme system integration (Tailwind, Bootstrap, Material, Fluent)
- Header and content area styling
- Resize handle styling and customization
- Panel backgrounds, borders, and gradients
- Dragging and interaction state styling
- Advanced customization patterns
🎮 Interaction & Behavior
📄 Read: references/dragging-behavior.md
- Enable and configure drag functionality
- Drag events (dragStart, drag, dragStop)
- Collision detection and panel pushing
- Custom drag handles (header-only, icon-only, multiple)
- Preventing specific panels from dragging
- Visual feedback and placeholder styling
- Programmatic panel movement
- Undo/redo implementation
📄 Read: references/resizing-floating.md
- Enable and configure resize functionality
- Resize handle directions (SE, E, W, N, S, NW, NE, SW)
- Size constraints (minSizeX, minSizeY, maxSizeX, maxSizeY)
- Resize events (resizeStart, resize, resizeStop)
- Floating behavior and gap filling
- Programmatic resizing (resizePanel method)
- Responsive resizing and aspect ratio maintenance
- Best practices for resizing
⚙️ Configuration & Layout
📄 Read: references/cell-configuration.md
- Grid columns and cell distribution
- Cell sizing basics (sizeX, sizeY)
- Cell aspect ratio configuration
- Cell spacing and gap management
- Cell calculation examples
- Responsive cell configuration
- Masonry, hero, and nested grid patterns
📄 Read: references/responsive-design.md
- Built-in responsive behavior
- Media query configuration and custom breakpoints
- Standard breakpoint systems
- Adaptive layouts for different screen sizes
- Mobile-first approach
- Touch device support and optimization
- Testing responsive layouts
- Performance optimization for responsiveness
💾 Advanced Features
📄 Read: references/state-persistence.md
- Serialize method for saving layout
- Save/restore state patterns
- localStorage integration
- sessionStorage integration
- Database API integration
- Advanced state management (Redux, Context API)
- Versioning and migration
- Error recovery and backups
📄 Read: references/methods-reference.md
- All 9 component methods documented
- Panel management: addPanel, removePanel, removeAll, updatePanel
- Layout manipulation: movePanel, resizePanel
- Serialization: serialize method
- Utility methods: refreshDraggableHandle, destroy
- Complete method examples and use cases
📄 Read: references/events-reference.md
- All 10 component events documented
- Lifecycle events: created, destroyed
- Interaction events: dragStart, drag, dragStop, resizeStart, resize, resizeStop
- Change event: monitoring panel additions, removals, position changes
- Event arguments and properties
- Complete event handling patterns
♿ Compliance & Accessibility
📄 Read: references/accessibility-wcag.md
- WCAG 2.2 Level AA compliance
- Section 508 standards compliance
- WAI-ARIA implementation (roles, properties, states)
- Keyboard navigation and accessibility
- Screen reader support
- RTL (right-to-left) language support
- Testing and validation procedures
- Accessibility audit checklist
Quick Start Example
Create an interactive dashboard in minutes:
import React, { useRef } from 'react';
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
import '@syncfusion/ej2-base/styles/tailwind3.css';
import '@syncfusion/ej2-react-layouts/styles/tailwind3.css';
function Dashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const panels = [
{
id: 'sales',
header: 'Sales',
content: '<div style="padding: 20px;">Sales data</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
},
{
id: 'users',
header: 'Users',
content: '<div style="padding: 20px;">User analytics</div>',
row: 0,
col: 2,
sizeX: 2,
sizeY: 2
},
{
id: 'reports',
header: 'Reports',
content: '<div style="padding: 20px;">Report metrics</div>',
row: 2,
col: 0,
sizeX: 4,
sizeY: 1
}
];
return (
<div style={{ padding: '20px' }}>
<h1>Dashboard</h1>
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
columns={5}
cellSpacing={[10, 10]}
panels={panels}
allowDragging={true}
allowResizing={true}
allowFloating={true}
enablePersistence={true}
>
</DashboardLayoutComponent>
</div>
);
}
export default Dashboard;Common Patterns
Pattern 1: Responsive Dashboard with Mobile Support
📖 Read: responsive-design.md for comprehensive mobile adaptation patterns
<DashboardLayoutComponent
id='responsive-dashboard'
columns={5}
mediaQuery='max-width:768px' // Single column on tablets/mobile
cellSpacing={[10, 10]}
allowDragging={true}
allowResizing={true}
panels={panels}
>
</DashboardLayoutComponent>Key Features:
- Responsive column adjustment based on screen size
- Mobile-first design with media queries
- Touch optimization for mobile devices
- See
responsive-design.mdfor breakpoint configuration and testing
Pattern 2: Save and Restore User Layout
📖 Read: state-persistence.md for complete state serialization and storage options
const handleSaveLayout = () => {
const layout = dashboardRef.current?.serialize();
localStorage.setItem('userDashboard', JSON.stringify(layout));
};
const handleRestoreLayout = () => {
const saved = localStorage.getItem('userDashboard');
if (saved) {
const layout = JSON.parse(saved);
dashboardRef.current?.removeAll();
layout.forEach(panel => {
dashboardRef.current?.addPanel(panel);
});
}
};Key Features:
- serialize() method exports current layout state
- localStorage for client-side persistence
- See
state-persistence.mdfor API integration, Redux/Context patterns, and auto-save
Pattern 3: Dynamic Panel Management
📖 Read: core-functionality.md for panel addition/removal and runtime management
const handleAddPanel = () => {
dashboardRef.current?.addPanel({
id: `panel-${Date.now()}`,
header: 'New Panel',
content: 'Panel content',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
});
};
const handleRemovePanel = (panelId: string) => {
dashboardRef.current?.removePanel(panelId);
};Key Features:
- Runtime panel creation and deletion
- Unique panel IDs and positioning
- Event handling for panel changes
- See
methods-reference.mdfor all available panel methods
Pattern 4: Constrained Panel Sizes
📖 Read: cell-configuration.md and resizing-floating.md for size constraints and resizing behavior
const constrainedPanels = [
{
id: 'fixed-panel',
header: 'Fixed Size',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1,
minSizeX: 2, // Cannot shrink
maxSizeX: 2, // Cannot expand
minSizeY: 1,
maxSizeY: 1
},
{
id: 'flexible-panel',
header: 'Flexible',
row: 0,
col: 2,
sizeX: 2,
sizeY: 1,
minSizeX: 1, // Can shrink to 1 cell
maxSizeX: 4 // Can expand to 4 cells
}
];Key Features:
- minSizeX/Y and maxSizeX/Y constraints
- Fixed vs. flexible panel sizing
- Programmatic size updates via resizePanel()
- See
cell-configuration.mdfor grid calculations andresizing-floating.mdfor advanced resize patterns
Pattern 5: Monitor Layout Changes
📖 Read: events-reference.md for all available events and handlers
const handleChange = (args: ChangeEventArgs) => {
console.log('Panels added:', args.addedPanels);
console.log('Panels removed:', args.removedPanels);
console.log('Panels position changed:', args.changedPanels);
console.log('User interaction:', args.isInteracted);
};
<DashboardLayoutComponent
panels={panels}
change={handleChange}
>
</DashboardLayoutComponent>Key Features:
- Change event fires on any layout modification
- Access to added, removed, and repositioned panels
- User interaction detection
- See
events-reference.mdfor drag, resize, and other event handlers
Key API Overview
Component Properties
📖 Full Reference: properties-reference.md
- columns - Number of grid columns (default: 1) → See
cell-configuration.md - cellSpacing - Spacing between panels [horizontal, vertical] → See
cell-configuration.md - panels - Array of PanelModel objects defining layout → See
panel-templates.md - allowDragging - Enable panel drag-and-drop (default: true) → See
dragging-behavior.md - allowResizing - Enable panel resizing (default: false) → See
resizing-floating.md - allowFloating - Auto-fill empty spaces (default: true) → See
resizing-floating.md - enablePersistence - Save layout state (default: false) → See
state-persistence.md - mediaQuery - Responsive breakpoint for mobile layouts → See
responsive-design.md - draggableHandle - Custom drag handle selector → See
dragging-behavior.md - resizableHandles - Resize handle directions → See
resizing-floating.md - cssClass - Custom CSS classes → See
styling-customization.md
Core Methods
📖 Full Reference: methods-reference.md
- addPanel(panel) - Add new panel at runtime → See
core-functionality.md - removePanel(id) - Remove panel by ID → See
core-functionality.md - removeAll() - Clear all panels → See
core-functionality.md - updatePanel(panel) - Modify existing panel → See
core-functionality.md - movePanel(id, row, col) - Change panel position → See
dragging-behavior.md - resizePanel(id, sizeX, sizeY) - Adjust panel dimensions → See
resizing-floating.md - serialize() - Get current layout state → See
state-persistence.md
Important Events
📖 Full Reference: events-reference.md
- created - Component initialized
- dragStart / drag / dragStop - Drag operations → See
dragging-behavior.md - resizeStart / resize / resizeStop - Resize operations → See
resizing-floating.md - change - Layout changed (additions, removals, movements) → See
events-reference.md
Styling & Customization
📖 Full Reference: styling-customization.md
- CSS selectors for all panel elements
- Theme customization (Tailwind, Bootstrap, Material, Fluent)
- Responsive styling patterns
- Accessibility styling (WCAG 2.2)
Common Use Cases
🎯 Creating Dashboards: Implement customizable dashboard layouts with multiple content panels organized in a grid.
- Read: core-functionality.md, panel-templates.md
🎯 User-Customizable Layouts: Allow users to rearrange and resize dashboard panels to their preference with automatic saving.
- Read: dragging-behavior.md, resizing-floating.md, state-persistence.md
🎯 Responsive Interfaces: Build layouts that adapt from multi-column desktop views to single-column mobile views.
- Read: responsive-design.md, cell-configuration.md
🎯 Real-Time Monitoring: Display multiple real-time data sources in resizable panels with live updates.
- Read: panel-templates.md (component embedding), events-reference.md
🎯 Admin Panels: Create administrative interfaces with draggable widgets for system monitoring and control.
- Read: dragging-behavior.md, styling-customization.md, accessibility-wcag.md
🎯 Data Visualization: Organize multiple charts, tables, and metrics in flexible, responsive panel layouts.
- Read: panel-templates.md (embedding charts/grids), styling-customization.md
Panel Model Interface
interface PanelModel {
id: string; // Unique identifier (required)
row: number; // Row position (required)
col: number; // Column position (required)
sizeX: number; // Width in cells (default: 1)
sizeY: number; // Height in cells (default: 1)
header?: string | HTMLElement | Function;
content?: string | HTMLElement | Function;
cssClass?: string; // Custom CSS classes
enabled?: boolean; // Enable/disable (default: true)
minSizeX?: number; // Minimum width (default: 1)
minSizeY?: number; // Minimum height (default: 1)
maxSizeX?: number; // Maximum width
maxSizeY?: number; // Maximum height
zIndex?: number; // Stacking order
}Quick Reference Guide
| Task | Read This Documentation |
|---|---|
| Get Started | getting-started.md - Installation, setup, themes |
| Learn Panel Management | core-functionality.md - Create, update, remove panels |
| Customize Panel Content | panel-templates.md - Headers, templates, component embedding |
| Make It Draggable | dragging-behavior.md - Drag events, collision, custom handles |
| Make It Resizable | resizing-floating.md - Resize handles, constraints, floating |
| Configure Grid System | cell-configuration.md - Columns, spacing, aspect ratio, responsive grids |
| Make It Responsive | responsive-design.md - Breakpoints, mobile, touch optimization |
| Save User Layouts | state-persistence.md - serialize(), localStorage, API, Redux, Context |
| Style & Theme | styling-customization.md - CSS selectors, themes, responsive styling |
| Make It Accessible | accessibility-wcag.md - WCAG 2.2, WAI-ARIA, screen readers, RTL |
| API Reference | properties-reference.md, methods-reference.md, events-reference.md |
| Advanced Patterns | advanced-features.md - Custom integrations, performance optimization |
Implementation Roadmap
Phase 1 - Foundation (Start Here): 1. Read getting-started.md for setup 2. Review core-functionality.md for panel basics 3. Check properties-reference.md for all available options
Phase 2 - Interaction (Build Functionality): 4. Implement dragging: dragging-behavior.md 5. Implement resizing: resizing-floating.md 6. Configure grid: cell-configuration.md
Phase 3 - Content (Customize Visuals): 7. Create panel templates: panel-templates.md 8. Apply styling: styling-customization.md
Phase 4 - Polish (Production Ready): 9. Optimize for mobile: responsive-design.md 10. Add accessibility: accessibility-wcag.md 11. Implement persistence: state-persistence.md
Phase 5 - Advanced (Expert Features): 12. Explore patterns: advanced-features.md 13. Refer to event/method APIs: events-reference.md, methods-reference.md
Accessibility and WCAG Compliance
Table of Contents
- Overview
- WCAG 2.2 Compliance
- Section 508 Standards
- WAI-ARIA Implementation
- Keyboard Navigation
- Screen Reader Support
- RTL Support
- Testing and Validation
Overview
Dashboard Layout is designed with accessibility as a core principle, following industry standards and best practices. The component supports WCAG 2.2 Level AA compliance, Section 508 accessibility standards, and full WAI-ARIA implementation.
Accessibility Standards Supported
| Standard | Version | Coverage | Status |
|---|---|---|---|
| WCAG | 2.2 | Level AA | ✅ Full |
| Section 508 | 2023 | Technical Standards | ✅ Full |
| WAI-ARIA | 1.2 | Authoring Practices | ✅ Full |
| ATAG | 2.0 | Authoring Tools | ✅ Supported |
WCAG 2.2 Compliance
Dashboard Layout implements all WCAG 2.2 Level AA requirements:
Perceivable
1.4.3 Contrast (Minimum)
- Text contrast ratio: 4.5:1 for normal text
- UI component contrast: 3:1 minimum
- Decorative elements: no contrast requirement
/* High contrast panel header */
.e-panel .e-panel-header {
background: #212121;
color: #ffffff;
/* Contrast ratio: 21:1 ✅ */
}
.e-panel .e-panel-content {
background: #ffffff;
color: #333333;
/* Contrast ratio: 10.5:1 ✅ */
}1.4.11 Non-Text Contrast
- All UI controls meet 3:1 minimum contrast
- Graphical elements clearly distinguishable
// Resize handle with high contrast
const panels = [
{
id: 'accessible-panel',
header: 'Accessible Panel',
content: '<div>Content</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
// CSS
.e-panel .e-south-east {
background: linear-gradient(135deg, transparent 50%, #0066cc 50%);
/* Clear contrast against white background */
}Operable
2.1.1 Keyboard (Level A)
- All functionality available via keyboard
- No keyboard trap
- Focus order logical and intuitive
2.4.3 Focus Order
- Tab navigation follows logical reading order
- Focus is always visible
function AccessibleDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
return (
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
tabIndex={0}
/* Focus management automatic */
/>
);
}2.4.7 Focus Visible
- Focus indicator always visible
- Minimum 2px wide outline
/* Visible focus indicator */
.e-panel:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
.e-panel .e-panel-header:focus-visible {
outline: 2px solid #0066cc;
}
.e-panel .e-resize-handle:focus-visible {
outline: 2px solid #0066cc;
border-radius: 2px;
}Understandable
3.2.1 On Focus
- Dashboard doesn't change context on focus
- No automatic submissions or navigations
3.2.4 Consistent Identification
- Components consistently identified
- Resize handles always in same location
- Drag handles always accessible
Robust
4.1.3 Status Messages
- Live regions announce changes
- Drag and drop events communicated
// Announce panel changes to screen readers
function AccessibleDashboard() {
const handlePanelRemove = (args: any) => {
const announcement = `Panel ${args.id} removed`;
const liveRegion = document.getElementById('announcements');
if (liveRegion) {
liveRegion.textContent = announcement;
}
};
return (
<>
<div id='announcements' aria-live='polite' aria-atomic='true' role='status'/>
<DashboardLayoutComponent
id='dashboard'
panels={panels}
change={handlePanelRemove}
columns={5}
/>
</>
);
}Section 508 Standards
Section 508 Amendment to the Rehabilitation Act requires federal agencies to ensure IT is accessible:
Technical Standards Compliance
§ 1194.22 – Web-based Intranet and Internet Information
// Accessible panel with semantic structure
function AccessibleWebDashboard() {
const panels = [
{
id: 'report-panel',
header: 'Sales Report',
content: `
<section aria-label="Sales Report Section">
<article role="article">
<h2>Q1 Results</h2>
<p>Sales exceeded targets by 15%</p>
<table role="presentation" aria-label="Sales data table">
<thead>
<tr>
<th>Region</th>
<th>Revenue</th>
<th>Growth</th>
</tr>
</thead>
<tbody>
<tr>
<td>North</td>
<td>$50,000</td>
<td>+12%</td>
</tr>
</tbody>
</table>
</article>
</section>
`,
row: 0,
col: 0,
sizeX: 3,
sizeY: 2
}
];
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
enableRtl={false}
/>
);
}Color Not Sole Identifier
- Don't use color alone to convey information
- Use text labels, patterns, or icons
// ❌ AVOID: Color-only identification
const colorOnlyPanel = (
<div style={{ color: 'red' }}>Error status</div>
);
// ✅ RECOMMENDED: Color + Text + Icon
const accessiblePanel = (
<div style={{ color: 'red' }}>
<span aria-label="Error">❌</span> Error: Data load failed
</div>
);WAI-ARIA Implementation
Dashboard Layout uses WAI-ARIA to enhance semantic meaning:
Roles
// Panel as list item
const ariaRolePanel = (
<div role='listitem' aria-label='Dashboard panel'>
{/* Panel content */}
</div>
);
// Dashboard as list
const ariaDashboard = (
<div role='list' aria-label='Dashboard panels'>
{/* Multiple panels with role='listitem' */}
</div>
);
// Resize handle role
const resizeHandleRole = (
<div
role='slider'
aria-label='Panel resize handle'
aria-valuemin='0'
aria-valuemax='5'
aria-valuenow='2'
/>
);ARIA Properties
aria-grabbed - Indicates draggable state
// Draggable panel indication
function DraggablePanel() {
const [isDragging, setIsDragging] = useState(false);
return (
<div
role='listitem'
aria-grabbed={isDragging}
aria-dropeffect='move'
onMouseDown={() => setIsDragging(true)}
onMouseUp={() => setIsDragging(false)}
>
Panel Content
</div>
);
}aria-label & aria-labelledby
// Explicit labeling
const panels = [
{
id: 'sales-panel',
header: 'Sales Dashboard',
content: '<div role="region" aria-labelledby="sales-header">Sales data</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
// Or using aria-label
<DashboardLayoutComponent
id='dashboard'
aria-label='Main dashboard with draggable sales panels'
panels={panels}
/>aria-live - Announce dynamic changes
function AccessibleDashboard() {
return (
<>
<div
id='panel-announcements'
aria-live='polite'
aria-atomic='true'
role='status'
className='sr-only'
/>
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
change={(args) => {
const region = document.getElementById('panel-announcements');
region!.textContent = `Panel ${args.id} ${args.type}`;
}}
/>
</>
);
}aria-disabled - Disabled state
// Prevent dragging with ARIA
<div
role='button'
aria-disabled='true'
aria-label='Cannot drag this panel'
className='e-panel disabled'
>
Locked Panel
</div>Keyboard Navigation
Keyboard Shortcuts
| Key | Action | Context |
|---|---|---|
| Tab | Navigate between panels | General |
| Shift+Tab | Navigate backwards | General |
| Enter | Activate focused panel | Panel header |
| Space | Toggle panel state | Panel header |
| Arrow Keys | Adjust size (resize mode) | Resize handle |
| Escape | Cancel drag/resize | During operation |
Implementation Example
function AccessibleDashboardWithKeyboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const [focusedPanelId, setFocusedPanelId] = useState<string | null>(null);
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
// Cancel ongoing operations
setFocusedPanelId(null);
}
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
// Adjust panel size
e.preventDefault();
const increment = e.key === 'ArrowUp' ? 1 : -1;
// Update panel dimensions
}
};
return (
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
tabIndex={0}
onKeyDown={handleKeyDown}
/>
);
}Focus Management
function DashboardWithFocusManagement() {
const firstPanelRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Set initial focus on first panel
firstPanelRef.current?.focus();
}, []);
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
/* First panel receives initial focus */
/>
);
}Screen Reader Support
Semantic HTML Structure
// Proper semantic structure
const panels = [
{
id: 'main-report',
header: 'Main Report',
content: `
<main role='main' aria-label='Main dashboard report'>
<section aria-labelledby='report-title'>
<h2 id='report-title'>Q1 Performance</h2>
<article>
<p>Report content with semantic structure</p>
</article>
</section>
</main>
`,
row: 0,
col: 0,
sizeX: 3,
sizeY: 2
}
];Screen Reader Testing
Testing Tools:
- NVDA (Windows) - Free, open source
- JAWS (Windows) - Commercial
- VoiceOver (Mac/iOS) - Built-in
- TalkBack (Android) - Built-in
// Test with screen reader announcements
function ScreenReaderTestDashboard() {
const [message, setMessage] = useState('');
return (
<>
{/* Hidden but announced to screen readers */}
<div role='status' aria-live='polite' aria-atomic='true'>
{message}
</div>
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
change={(args) => {
setMessage(`Panel changed: ${args.id}`);
}}
/>
</>
);
}Alt Text for Images in Panels
const panels = [
{
id: 'image-panel',
header: 'Product Image',
content: `
<figure>
<img
src='product.jpg'
alt='Premium dashboard widget with blue gradient'
style='max-width: 100%; height: auto;'
/>
<figcaption>Product featured in Q1 campaign</figcaption>
</figure>
`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];RTL Support
Dashboard Layout supports Right-to-Left languages (Arabic, Hebrew, Farsi, etc.):
// Enable RTL mode
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
enableRtl={true}
/>RTL Styling
/* RTL-aware styling */
[dir='rtl'] .e-panel .e-panel-header {
text-align: right;
padding-right: 16px;
padding-left: 12px;
}
[dir='rtl'] .e-panel .e-south-west {
right: auto;
left: 0;
}
[dir='rtl'] .e-panel .e-south-east {
right: auto;
left: 0;
}Language-Specific Considerations
function MultilingualDashboard() {
const [language, setLanguage] = useState('en');
const isRTL = language === 'ar' || language === 'he';
const panels = [
{
id: 'multilingual-panel',
header: getHeaderText(language),
content: getContentText(language),
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
return (
<div dir={isRTL ? 'rtl' : 'ltr'} lang={language}>
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
enableRtl={isRTL}
/>
</div>
);
}Testing and Validation
Automated Testing
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('Dashboard should not have accessibility violations', async () => {
const { container } = render(
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
/>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});Manual Testing Checklist
- ✅ Tab through all panels - can you navigate all interactive elements?
- ✅ Drag panels with keyboard - Tab to handle, use arrow keys
- ✅ Resize with keyboard - Focus handle, arrow keys adjust size
- ✅ Screen reader testing - Announcement of panel changes
- ✅ Color contrast - Use color contrast analyzer (>4.5:1)
- ✅ Zoom testing - Set zoom to 200%, layout should adapt
- ✅ RTL testing - Switch to RTL mode, verify mirroring
- ✅ Focus visible - Tab through, outline always visible
- ✅ Keyboard traps - Escape should cancel operations
- ✅ Alternative text - Images have descriptive alt text
Accessibility Audit Tools
- Axe DevTools - Browser extension for violations
- Lighthouse - Built into Chrome DevTools
- WAVE - WebAIM accessibility checker
- Color Contrast Analyzer - Contrast verification
- Screen readers - NVDA, VoiceOver, JAWS
WCAG 2.2 Audit Checklist
## Critical (Must Fix)
- [ ] All interactive elements keyboard accessible
- [ ] Focus indicator always visible (min 2px)
- [ ] Color contrast minimum 4.5:1
- [ ] No keyboard traps
- [ ] Meaningful alt text on images
## Important (Should Fix)
- [ ] Logical tab order
- [ ] ARIA roles correctly implemented
- [ ] Live regions announce changes
- [ ] Error messages clear and actionable
- [ ] Resize handles clearly identifiable
## Nice to Have
- [ ] High contrast mode support
- [ ] Text resize support (up to 200%)
- [ ] Customizable colors
- [ ] Reduced motion supportARIA Validation
// Ensure all required ARIA attributes present
function ValidateAriaCompliance() {
const requiredAttributes = {
'role': true,
'aria-label': true,
'aria-labelled-by': true,
'aria-live': true
};
const panel = document.querySelector('.e-panel');
Object.keys(requiredAttributes).forEach(attr => {
if (requiredAttributes[attr]) {
if (!panel?.hasAttribute(attr)) {
console.warn(`Missing ${attr} attribute`);
}
}
});
}Dashboard Layout Advanced Features
Table of Contents
- Dynamic Panel Creation
- Custom Styling & CSS
- Panel Templates
- Accessibility & RTL
- Performance Optimization
- Integration Patterns
Dynamic Panel Creation
Add Panels at Runtime
Add panels dynamically after component initialization:
import { useRef } from 'react';
import { DashboardLayoutComponent, PanelModel } from '@syncfusion/ej2-react-layouts';
export const DynamicPanelManager = () => {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const addNewPanel = () => {
const newPanel: PanelModel = {
id: `panel-${Date.now()}`,
header: 'New Panel',
content: '<div style="padding: 20px;">New panel content</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
};
dashboardRef.current?.addPanel(newPanel);
};
return (
<>
<button onClick={addNewPanel}>Add New Panel</button>
<DashboardLayoutComponent
ref={dashboardRef}
columns={5}
panels={[]}
>
</DashboardLayoutComponent>
</>
);
};Remove Panels at Runtime
Remove specific panels or all panels:
const removePanel = (panelId: string) => {
dashboardRef.current?.removePanel(panelId);
};
const clearAllPanels = () => {
dashboardRef.current?.removeAll();
};Batch Panel Operations
Add/remove multiple panels efficiently:
const addMultiplePanels = (panelConfigs: PanelModel[]) => {
panelConfigs.forEach(config => {
dashboardRef.current?.addPanel(config);
});
};
const replaceAllPanels = (newPanels: PanelModel[]) => {
dashboardRef.current?.removeAll();
newPanels.forEach(panel => {
dashboardRef.current?.addPanel(panel);
});
};Custom Styling & CSS
Custom CSS Classes
Apply custom classes to panels:
const styledPanels: PanelModel[] = [
{
id: 'premium-panel',
header: 'Premium Panel',
cssClass: 'custom-premium highlight',
content: 'Premium content',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
},
{
id: 'alert-panel',
header: 'Alert',
cssClass: 'custom-alert',
content: 'Alert content',
row: 0,
col: 2,
sizeX: 2,
sizeY: 1
}
];
// CSS
const styles = `
.custom-premium {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 8px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}
.custom-alert {
background-color: #fee;
border-left: 4px solid #f44;
}
.highlight {
border: 2px solid gold;
}
`;Panel Header Customization
const customHeaderCSS = `
.e-dashboardlayout.e-control .e-panel .e-panel-container .e-panel-header {
background: linear-gradient(90deg, #667eea, #764ba2);
color: white;
font-weight: bold;
padding: 12px;
border-radius: 4px 4px 0 0;
}
.e-dashboardlayout.e-control .e-panel .e-panel-container .e-panel-header .e-icons {
color: white;
}
`;Panel Content Styling
const customContentCSS = `
.e-dashboardlayout.e-control .e-panel .e-panel-container .e-panel-content {
background-color: #f8f9fa;
padding: 20px;
border: 1px solid #dee2e6;
min-height: 100px;
}
.e-dashboardlayout.e-control .e-panel:hover .e-panel-container .e-panel-content {
background-color: #ffffff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
`;Resize Handle Styling
const customResizeCSS = `
.e-dashboardlayout.e-control .e-panel .e-panel-container .e-resize.e-double {
background-color: #667eea;
color: white;
font-size: 16px;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-items: center;
}
.e-dashboardlayout.e-control .e-panel .e-panel-container .e-resize.e-double:hover {
background-color: #764ba2;
cursor: grab;
}
`;Panel Templates
Header Templates
Create custom header content:
const headerTemplate = () => {
return (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>📊 Analytics</span>
<span style={{ fontSize: '12px', color: '#666' }}>Last updated: now</span>
</div>
);
};
const panelWithCustomHeader: PanelModel = {
id: 'analytics-panel',
header: headerTemplate, // Function returns JSX
content: '<div>Chart content</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
};Content Templates
Create complex panel content:
const contentTemplate = () => {
return (
<div style={{ padding: '20px' }}>
<h3>Sales Dashboard</h3>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
<div>
<p>Total Sales</p>
<h2>$45,000</h2>
</div>
<div>
<p>Growth</p>
<h2 style={{ color: 'green' }}>+12%</h2>
</div>
</div>
<button>View Details</button>
</div>
);
};
const panelWithComplexContent: PanelModel = {
id: 'sales-panel',
header: 'Sales',
content: contentTemplate, // Function returns JSX
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
};HTML String Templates
Use HTML strings for simple content:
const panelWithHTML: PanelModel = {
id: 'html-panel',
header: 'HTML Content',
content: `
<div style="padding: 20px;">
<h3>Dashboard Stats</h3>
<ul>
<li>Metric 1: 100</li>
<li>Metric 2: 200</li>
<li>Metric 3: 300</li>
</ul>
</div>
`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
};Accessibility & RTL
Accessibility (WCAG)
Enable keyboard navigation and screen reader support:
<DashboardLayoutComponent
id='accessible-dashboard'
panels={panels}
enableHtmlSanitizer={true} // Sanitize for security
>
</DashboardLayoutComponent>Keyboard Shortcuts:
- Tab: Navigate between panels
- Enter/Space: Interact with focused elements
- Arrow Keys: Move focus
- Escape: Cancel drag/resize operations
ARIA Attributes
Panels automatically include ARIA labels. For custom content:
const accessiblePanel: PanelModel = {
id: 'accessible-panel',
header: 'Sales Report',
content: `
<div aria-label="Sales Report Dashboard">
<h3 aria-level="3">Q4 Sales</h3>
<table role="table" aria-label="Quarterly sales data">
<tr>
<th>Month</th>
<th>Sales</th>
</tr>
<tr>
<td>October</td>
<td>$45,000</td>
</tr>
</table>
</div>
`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
};Right-to-Left (RTL) Layout
Enable RTL for Arabic, Hebrew, and other RTL languages:
<DashboardLayoutComponent
enableRtl={true} // Enable RTL rendering
mediaQuery='max-width:600px'
columns={5}
panels={panels}
>
</DashboardLayoutComponent>RTL CSS:
const rtlStyles = `
.e-dashboardlayout.e-rtl {
direction: rtl;
}
.e-dashboardlayout.e-rtl .e-panel .e-panel-header {
text-align: right;
}
`;Performance Optimization
Virtual Scrolling (Large Datasets)
For dashboards with many panels:
// Lazy load panels
const LazyDashboard = () => {
const [visiblePanels, setVisiblePanels] = useState<PanelModel[]>([]);
useEffect(() => {
// Load only visible panels
const loadedPanels = allPanels.slice(0, 10);
setVisiblePanels(loadedPanels);
// Load more on scroll
const handleScroll = () => {
if (scrollPosition > threshold) {
setVisiblePanels(prev => [...prev, ...allPanels.slice(prev.length, prev.length + 5)]);
}
};
return () => {};
}, []);
return (
<DashboardLayoutComponent
columns={5}
panels={visiblePanels}
>
</DashboardLayoutComponent>
);
};Memoization
Prevent unnecessary re-renders:
import { memo, useMemo } from 'react';
const DashboardMemo = memo(({ panels }) => {
const memoizedPanels = useMemo(() => panels, [panels]);
return (
<DashboardLayoutComponent
columns={5}
panels={memoizedPanels}
>
</DashboardLayoutComponent>
);
});Debounce Layout Changes
Reduce event firing:
import { useCallback, useRef } from 'react';
const DashboardWithDebounce = () => {
const debounceTimer = useRef<NodeJS.Timeout>();
const handleChange = useCallback((args: any) => {
clearTimeout(debounceTimer.current);
debounceTimer.current = setTimeout(() => {
console.log('Layout changed:', args);
saveLayout(args);
}, 300);
}, []);
return (
<DashboardLayoutComponent
panels={panels}
change={handleChange}
>
</DashboardLayoutComponent>
);
};Integration Patterns
Redux Integration
Manage dashboard state with Redux:
import { useSelector, useDispatch } from 'react-redux';
const ReduxDashboard = () => {
const dispatch = useDispatch();
const panels = useSelector((state: any) => state.dashboard.panels);
const handleChange = (args: any) => {
dispatch({
type: 'UPDATE_DASHBOARD_LAYOUT',
payload: args.changedPanels
});
};
return (
<DashboardLayoutComponent
panels={panels}
change={handleChange}
>
</DashboardLayoutComponent>
);
};API Integration
Load and save layouts from server:
const APIIntegratedDashboard = () => {
const [panels, setPanels] = useState<PanelModel[]>([]);
useEffect(() => {
// Load layout from API
fetch('/api/dashboard/layout')
.then(res => res.json())
.then(data => setPanels(data))
.catch(err => console.error('Failed to load layout:', err));
}, []);
const saveLayout = (newLayout: PanelModel[]) => {
fetch('/api/dashboard/layout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newLayout)
}).catch(err => console.error('Failed to save layout:', err));
};
return (
<DashboardLayoutComponent
panels={panels}
change={(args) => saveLayout(args.changedPanels)}
>
</DashboardLayoutComponent>
);
};Context API Integration
Share dashboard state across components:
import { createContext, useContext } from 'react';
const DashboardContext = createContext<any>(null);
export const DashboardProvider = ({ children }: any) => {
const [panels, setPanels] = useState<PanelModel[]>([]);
return (
<DashboardContext.Provider value={{ panels, setPanels }}>
{children}
</DashboardContext.Provider>
);
};
export const useDashboard = () => {
const context = useContext(DashboardContext);
if (!context) {
throw new Error('useDashboard must be used within DashboardProvider');
}
return context;
};
// Usage
const Dashboard = () => {
const { panels, setPanels } = useDashboard();
return (
<DashboardLayoutComponent
panels={panels}
change={(args) => setPanels(args.changedPanels)}
>
</DashboardLayoutComponent>
);
};Cell Configuration and Grid Sizing
Table of Contents
- Overview
- Grid Columns
- Cell Sizing Basics
- Cell Aspect Ratio
- Cell Spacing
- Cell Calculation Examples
- Responsive Cell Configuration
- Advanced Grid Patterns
- Best Practices
Overview
Dashboard Layout uses a grid-based system where panels are positioned in rows and columns. Cell configuration determines the visual dimensions of individual cells, affecting how panels are sized and positioned. Proper cell configuration is essential for creating well-proportioned, responsive dashboards.
Grid System Fundamentals
Dashboard (e.g., 1000px wide)
├─ Column 1 (200px) │ Column 2 (200px) │ Column 3 (200px) │ Column 4 (200px) │ Column 5 (200px)
│ │ │ │ │
├─ Row 1 (200px) │ Panel 1 (2x2) │ Panel 2 (1x2) │
│
├─ Row 2 (200px) │ [spans columns 1-2] │ [spans columns 4-5]
│
├─ Row 3 (200px) │ Panel 3 (4x1) │
│
└─ Row 4 (200px)Grid Columns
The columns property defines how many equal cells comprise each row:
<DashboardLayoutComponent
columns={5} // 5 equal cells per row
panels={panels}
/>Column Distribution
When columns={5}:
- Each row is divided into 5 equal cells
- Each cell width = Parent Width / 5
- Cell height = Cell Width (by default, unless cellAspectRatio is set)
Column Examples
function GridColumnExamples() {
return (
<div>
{/* 2-column grid (wide panels) */}
<DashboardLayoutComponent
id='grid-2'
columns={2}
panels={panels}
/>
{/* 4-column grid (medium panels) */}
<DashboardLayoutComponent
id='grid-4'
columns={4}
panels={panels}
/>
{/* 6-column grid (narrow panels) */}
<DashboardLayoutComponent
id='grid-6'
columns={6}
panels={panels}
/>
{/* 12-column grid (Bootstrap-like) */}
<DashboardLayoutComponent
id='grid-12'
columns={12}
panels={panels}
/>
</div>
);
}Column Sizing Impact
| Columns | Per-Column Width (1000px) | Typical Use |
|---|---|---|
| 2 | 500px | Large panels |
| 3 | 333px | Dashboard sections |
| 4 | 250px | Standard layout |
| 5 | 200px | Default |
| 6 | 166px | Compact layout |
| 8 | 125px | Very compact |
| 12 | 83px | Fine-grained control |
Cell Sizing Basics
Panel Size in Cells
Panels are sized using grid units (cells):
interface PanelModel {
sizeX: number; // Width in cells (columns)
sizeY: number; // Height in cells (rows)
}
const panels = [
{
id: 'wide-panel',
sizeX: 4, // Spans 4 columns
sizeY: 2, // Spans 2 rows
row: 0,
col: 0
}
];Size Examples
function CellSizeDemo() {
const panels = [
{
id: 'small',
header: 'Small (1x1)',
content: 'Small panel',
sizeX: 1,
sizeY: 1,
row: 0,
col: 0
},
{
id: 'wide',
header: 'Wide (3x1)',
content: 'Wide panel',
sizeX: 3,
sizeY: 1,
row: 0,
col: 1
},
{
id: 'tall',
header: 'Tall (1x3)',
content: 'Tall panel',
sizeX: 1,
sizeY: 3,
row: 0,
col: 4
},
{
id: 'large',
header: 'Large (2x2)',
content: 'Large panel',
sizeX: 2,
sizeY: 2,
row: 1,
col: 0
}
];
return (
<DashboardLayoutComponent
id='dashboard'
columns={5}
panels={panels}
/>
);
}Cell Aspect Ratio
The cellAspectRatio property defines the relationship between cell width and height:
// Property signature
cellAspectRatio: number; // Ratio of width to height (e.g., 100/50 = 2:1)Default Behavior
By default, cellAspectRatio is 1:1 (square cells):
<DashboardLayoutComponent
columns={5}
cellAspectRatio={1} // Default: 1:1 (width:height)
panels={panels}
/>Aspect Ratio Examples
function AspectRatioExamples() {
return (
<div>
{/* Square cells (1:1) */}
<div className="grid-container">
<h3>Square Cells (1:1)</h3>
<DashboardLayoutComponent
id='square'
columns={4}
cellAspectRatio={1} // width:height = 1:1
panels={panels}
/>
</div>
{/* Wide cells (2:1) */}
<div className="grid-container">
<h3>Wide Cells (2:1)</h3>
<DashboardLayoutComponent
id='wide'
columns={4}
cellAspectRatio={2} // width:height = 2:1
panels={panels}
/>
</div>
{/* Tall cells (1:2) */}
<div className="grid-container">
<h3>Tall Cells (1:2)</h3>
<DashboardLayoutComponent
id='tall'
columns={4}
cellAspectRatio={0.5} // width:height = 1:2
panels={panels}
/>
</div>
{/* Custom ratio (4:3 - 16:12) */}
<div className="grid-container">
<h3>Custom Cells (4:3)</h3>
<DashboardLayoutComponent
id='custom'
columns={4}
cellAspectRatio={4/3} // width:height = 4:3
panels={panels}
/>
</div>
</div>
);
}Calculating Actual Dimensions
Given:
- Parent width: 1000px
- Columns: 5
- cellAspectRatio: 100/50 (2:1)
Calculations:
Cell Width = 1000px / 5 = 200px
Cell Height = Cell Width / Aspect Ratio = 200px / 2 = 100px
Panel (2x2): 400px × 200pxCell Spacing
The cellSpacing property adds gaps between panels:
// Property signature
cellSpacing: [number, number]; // [horizontal, vertical] in pixelsSpacing Configuration
function CellSpacingExamples() {
return (
<div>
{/* No spacing */}
<DashboardLayoutComponent
id='no-space'
columns={5}
cellSpacing={[0, 0]}
panels={panels}
/>
{/* 10px spacing in all directions */}
<DashboardLayoutComponent
id='small-space'
columns={5}
cellSpacing={[10, 10]}
panels={panels}
/>
{/* 20px horizontal, 10px vertical */}
<DashboardLayoutComponent
id='custom-space'
columns={5}
cellSpacing={[20, 10]}
panels={panels}
/>
{/* Large spacing (20px) */}
<DashboardLayoutComponent
id='large-space'
columns={5}
cellSpacing={[20, 20]}
panels={panels}
/>
</div>
);
}Impact on Layout
No Spacing (cellSpacing=[0, 0]):
┌──────┬──────┬──────┐
│ │ │ │
├──────┼──────┼──────┤
│ │ │ │
└──────┴──────┴──────┘
With Spacing (cellSpacing=[10, 10]):
┌──────┐ ┌──────┐ ┌──────┐
│ │ │ │ │ │
└──────┘ └──────┘ └──────┘
↕ 10px spacing
┌──────┐ ┌──────┐ ┌──────┐
│ │ │ │ │ │
└──────┘ └──────┘ └──────┘Cell Calculation Examples
Example 1: Basic Layout
function BasicLayoutCalculation() {
const config = {
parentWidth: 1200,
columns: 6,
cellSpacing: [10, 10],
cellAspectRatio: 100/75 // 4:3 ratio
};
// Calculations
const cellWidth = config.parentWidth / config.columns; // 200px
const cellHeight = cellWidth / (100/75); // 150px
// Example panel
const panel = {
sizeX: 3, // 3 cells wide
sizeY: 2 // 2 cells tall
};
const panelWidth = (panel.sizeX * cellWidth) + ((panel.sizeX - 1) * config.cellSpacing[0]);
// = (3 * 200) + (2 * 10) = 620px
const panelHeight = (panel.sizeY * cellHeight) + ((panel.sizeY - 1) * config.cellSpacing[1]);
// = (2 * 150) + (1 * 10) = 310px
return (
<div>
<p>Cell Width: {cellWidth}px</p>
<p>Cell Height: {cellHeight}px</p>
<p>Panel (3x2) Size: {panelWidth}px × {panelHeight}px</p>
<DashboardLayoutComponent
id='dashboard'
columns={config.columns}
cellSpacing={config.cellSpacing}
cellAspectRatio={config.cellAspectRatio}
panels={panels}
/>
</div>
);
}Example 2: Responsive Layout
function ResponsiveLayoutCalculation() {
const [screenSize, setScreenSize] = useState({
width: window.innerWidth,
columns: 5
});
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
let columns = 5;
if (width < 640) columns = 2; // Mobile
else if (width < 1024) columns = 3; // Tablet
else if (width < 1280) columns = 4; // Desktop
else columns = 6; // Large desktop
setScreenSize({ width, columns });
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<div>
<p>Screen: {screenSize.width}px, Columns: {screenSize.columns}</p>
<DashboardLayoutComponent
id='dashboard'
columns={screenSize.columns}
cellSpacing={[10, 10]}
panels={panels}
/>
</div>
);
}Responsive Cell Configuration
Media Query Based Configuration
function MediaQueryDashboard() {
const [config, setConfig] = useState({
columns: 5,
cellAspectRatio: 100/75,
cellSpacing: [10, 10]
});
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
if (width < 480) {
// Mobile (extra small)
setConfig({
columns: 1,
cellAspectRatio: 100/100,
cellSpacing: [5, 5]
});
} else if (width < 768) {
// Mobile (small)
setConfig({
columns: 2,
cellAspectRatio: 100/100,
cellSpacing: [8, 8]
});
} else if (width < 1024) {
// Tablet
setConfig({
columns: 3,
cellAspectRatio: 100/75,
cellSpacing: [10, 10]
});
} else {
// Desktop
setConfig({
columns: 5,
cellAspectRatio: 100/75,
cellSpacing: [15, 15]
});
}
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<DashboardLayoutComponent
id='dashboard'
columns={config.columns}
cellAspectRatio={config.cellAspectRatio}
cellSpacing={config.cellSpacing}
panels={panels}
/>
);
}CSS Container Queries (Modern Approach)
function ContainerQueryDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
return (
<div className='dashboard-container'>
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
columns={5}
cellAspectRatio={100/75}
cellSpacing={[10, 10]}
panels={panels}
/>
</div>
);
}
// CSS
.dashboard-container {
container-type: inline-size;
}
/* Mobile layout */
@container (max-width: 640px) {
.e-dashboard-layout {
--grid-columns: 2;
}
}
/* Tablet layout */
@container (640px < width < 1024px) {
.e-dashboard-layout {
--grid-columns: 3;
}
}Advanced Grid Patterns
Pattern 1: Masonry Layout
function MasonryDashboard() {
const panels = [
// Column 1
{ id: 'p1', sizeX: 1, sizeY: 2, row: 0, col: 0 },
{ id: 'p2', sizeX: 1, sizeY: 1, row: 2, col: 0 },
// Column 2
{ id: 'p3', sizeX: 1, sizeY: 1, row: 0, col: 1 },
{ id: 'p4', sizeX: 1, sizeY: 2, row: 1, col: 1 },
// Column 3
{ id: 'p5', sizeX: 1, sizeY: 3, row: 0, col: 2 }
];
return (
<DashboardLayoutComponent
id='dashboard'
columns={3}
cellAspectRatio={100/100}
cellSpacing={[10, 10]}
allowFloating={true}
panels={panels}
/>
);
}Pattern 2: Hero + Grid
function HeroGridDashboard() {
const panels = [
// Hero section (full width)
{
id: 'hero',
header: 'Main Dashboard',
sizeX: 5,
sizeY: 2,
row: 0,
col: 0
},
// Supporting panels (3-column grid below)
{ id: 'p1', sizeX: 1, sizeY: 1, row: 2, col: 0 },
{ id: 'p2', sizeX: 2, sizeY: 1, row: 2, col: 1 },
{ id: 'p3', sizeX: 2, sizeY: 1, row: 2, col: 3 }
];
return (
<DashboardLayoutComponent
id='dashboard'
columns={5}
cellSpacing={[10, 10]}
panels={panels}
/>
);
}Pattern 3: Nested Grids
function NestedGridDashboard() {
const panels = [
// Large panel acts as container
{
id: 'container',
header: 'Analytics Section',
content: `
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; height: 100%;">
<div>Metric 1</div>
<div>Metric 2</div>
<div>Metric 3</div>
<div>Metric 4</div>
</div>
`,
sizeX: 3,
sizeY: 2,
row: 0,
col: 0
},
// Adjacent small panels
{ id: 'side1', sizeX: 1, sizeY: 1, row: 0, col: 3 },
{ id: 'side2', sizeX: 1, sizeY: 1, row: 1, col: 3 }
];
return (
<DashboardLayoutComponent
id='dashboard'
columns={4}
cellSpacing={[10, 10]}
panels={panels}
/>
);
}Best Practices
1. Choose Appropriate Column Count
// ✅ RECOMMENDED: Match your design system
// Use 2, 3, 4, 5, or 12 columns (common grid systems)
<DashboardLayoutComponent columns={5} panels={panels} />
// ❌ AVOID: Too many columns makes layout complex
<DashboardLayoutComponent columns={24} panels={panels} />2. Set Consistent Cell Aspect Ratio
// ✅ RECOMMENDED: Standard aspect ratios
const ratios = {
square: 1, // 1:1
widescreen: 16/9, // 16:9
cinema: 21/9, // 21:9
photoWide: 3/2, // 3:2
video: 4/3, // 4:3
golden: 1.618 // Golden ratio
};
<DashboardLayoutComponent
cellAspectRatio={ratios.video}
panels={panels}
/>3. Use Appropriate Spacing
// ✅ RECOMMENDED: 10-20px spacing for visual clarity
<DashboardLayoutComponent
cellSpacing={[15, 15]} // Balanced spacing
panels={panels}
/>
// ⚠️ TOO MUCH: Creates large gaps
<DashboardLayoutComponent
cellSpacing={[40, 40]}
panels={panels}
/>
// ⚠️ TOO LITTLE: Panels appear cramped
<DashboardLayoutComponent
cellSpacing={[0, 0]}
panels={panels}
/>4. Plan Responsive Breakpoints
// ✅ RECOMMENDED: Standard breakpoints
const breakpoints = {
xs: { width: 320, columns: 1 },
sm: { width: 640, columns: 2 },
md: { width: 1024, columns: 3 },
lg: { width: 1280, columns: 5 },
xl: { width: 1920, columns: 6 }
};5. Document Grid Configuration
interface DashboardConfig {
// Grid settings
columns: number;
cellAspectRatio: number;
cellSpacing: [number, number];
// Behavior
allowDragging: boolean;
allowResizing: boolean;
allowFloating: boolean;
// Documentation
description: string;
breakpoint: string;
}
const desktopConfig: DashboardConfig = {
columns: 5,
cellAspectRatio: 100/75,
cellSpacing: [15, 15],
allowDragging: true,
allowResizing: true,
allowFloating: true,
description: 'Full-featured desktop layout',
breakpoint: '>= 1280px'
};Dashboard Layout Core Functionality
Table of Contents
- Panel Management
- Dragging & Positioning
- Resizing
- Floating & Auto-Arrangement
- Grid Configuration
- Responsive Behavior
- State Persistence
Panel Management
Creating Panels
Define panels with the PanelModel interface:
interface PanelModel {
id: string; // Unique identifier (required)
row: number; // Row position (required)
col: number; // Column position (required)
sizeX: number; // Width in cells (default: 1)
sizeY: number; // Height in cells (default: 1)
header?: string | HTMLElement | Function; // Panel header
content?: string | HTMLElement | Function; // Panel content
cssClass?: string; // Custom CSS classes
enabled?: boolean; // Enable/disable panel (default: true)
minSizeX?: number; // Minimum width (default: 1)
minSizeY?: number; // Minimum height (default: 1)
maxSizeX?: number; // Maximum width (default: null)
maxSizeY?: number; // Maximum height (default: null)
zIndex?: number; // Z-index stacking (default: 1000)
}Panel Configuration Example
const panels: PanelModel[] = [
{
id: 'analytics-panel',
row: 0,
col: 0,
sizeX: 3,
sizeY: 2,
header: 'Analytics Dashboard',
content: '<div>Chart content here</div>',
minSizeX: 2,
minSizeY: 1,
maxSizeX: 4,
maxSizeY: 3,
cssClass: 'custom-analytics'
},
{
id: 'reports-panel',
row: 0,
col: 3,
sizeX: 2,
sizeY: 2,
header: 'Reports',
content: '<div>Report content here</div>',
enabled: true
}
];Disable Specific Panels
// Make panel non-interactive
const disabledPanel: PanelModel = {
id: 'read-only-panel',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1,
header: 'Read Only',
content: 'This panel is locked',
enabled: false // Panel won't respond to drag/resize
};Panel Size Constraints
// Enforce minimum and maximum sizes
const constrainedPanel: PanelModel = {
id: 'constrained-panel',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1,
minSizeX: 1, // Cannot be smaller than 1 cell width
minSizeY: 1, // Cannot be smaller than 1 cell height
maxSizeX: 4, // Cannot exceed 4 cells width
maxSizeY: 3 // Cannot exceed 3 cells height
};Dragging & Positioning
Enable Dragging
Dragging is enabled by default. Control it with allowDragging property:
<DashboardLayoutComponent
allowDragging={true} // Enable dragging
panels={panels}
>
</DashboardLayoutComponent>Draggable Handle
Restrict dragging to specific elements using draggableHandle:
<DashboardLayoutComponent
allowDragging={true}
draggableHandle='.e-panel-header' // Only header can be dragged
panels={panels}
>
</DashboardLayoutComponent>// HTML Implementation
<div id='panel1' className='e-panel' data-row='0' data-col='0' data-sizex='2' data-sizey='1'>
<div className='e-panel-header'>Panel 1</div> {/* Only this is draggable */}
<div className='e-panel-content'>Content</div>
</div>Programmatic Panel Movement
Move panels using the movePanel() method:
const dashboardRef = useRef<DashboardLayoutComponent>(null);
// Move panel to specific position
const handleMovePanel = () => {
dashboardRef.current?.movePanel('panel1', 1, 2); // row: 1, col: 2
};
<DashboardLayoutComponent ref={dashboardRef} panels={panels}>
</DashboardLayoutComponent>Reorder Panels Logic
const reorderPanels = (dashboardRef: any) => {
// Move all panels one position to the right
dashboardRef.current?.movePanel('panel1', 0, 1);
dashboardRef.current?.movePanel('panel2', 0, 3);
dashboardRef.current?.movePanel('panel3', 0, 5);
};Resizing
Enable Resizing
Enable resizing with the allowResizing property:
<DashboardLayoutComponent
allowResizing={true} // Enable resize handles
resizableHandles={['e-south-east', 'e-south', 'e-east']}
panels={panels}
>
</DashboardLayoutComponent>Resize Handles
Configure which edges/corners can be dragged to resize:
<DashboardLayoutComponent
allowResizing={true}
resizableHandles={[
'e-south-east', // Bottom-right corner
'e-south', // Bottom edge
'e-east', // Right edge
'e-north-east', // Top-right corner
'e-west' // Left edge
]}
panels={panels}
>
</DashboardLayoutComponent>Programmatic Resizing
Resize panels using the resizePanel() method:
const handleResizePanel = () => {
// Resize panel to 3 columns wide x 2 rows high
dashboardRef.current?.resizePanel('panel1', 3, 2);
};Size Constraints Example
const constrainedPanels: PanelModel[] = [
{
id: 'fixed-panel',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1,
minSizeX: 2, // Cannot shrink below 2 cells
minSizeY: 1,
maxSizeX: 2, // Cannot expand beyond 2 cells
maxSizeY: 1,
header: 'Fixed Size Panel'
},
{
id: 'flexible-panel',
row: 0,
col: 2,
sizeX: 1,
sizeY: 1,
minSizeX: 1,
minSizeY: 1,
maxSizeX: 5, // Can expand up to 5 cells
maxSizeY: 4,
header: 'Flexible Panel'
}
];Floating & Auto-Arrangement
Enable Floating
When enabled, panels automatically move upward to fill empty spaces:
<DashboardLayoutComponent
allowFloating={true} // Enable auto-arrangement
panels={panels}
>
</DashboardLayoutComponent>Behavior:
- When a panel is dragged or resized, others automatically move to fill empty cells
- Creates compact layout without gaps
- Improves space utilization
Floating Example
// Initial layout:
// [Panel1] [Panel3]
// [Panel2] [Empty]
// After dragging Panel1 down with floating enabled:
// [Panel3] [Empty]
// [Panel1] [Panel2] ← Panel2 auto-moves up
<DashboardLayoutComponent
allowFloating={true}
panels={panels}
dragStop={() => console.log('Panels auto-arranged')}
>
</DashboardLayoutComponent>Grid Configuration
Column Layout
Configure the number of columns in the dashboard:
<DashboardLayoutComponent
columns={5} // 5-column grid
panels={panels}
>
</DashboardLayoutComponent>Cell Spacing
Control horizontal and vertical spacing between panels:
<DashboardLayoutComponent
columns={5}
cellSpacing={[10, 10]} // [horizontal, vertical] spacing in pixels
panels={panels}
>
</DashboardLayoutComponent>Common Spacing Values:
// Compact layout
cellSpacing={[5, 5]}
// Moderate spacing
cellSpacing={[10, 10]}
// Spacious layout
cellSpacing={[20, 20]}
// Asymmetric spacing
cellSpacing={[15, 5]} // More horizontal than verticalCell Aspect Ratio
Control the height-to-width ratio of grid cells:
<DashboardLayoutComponent
columns={5}
cellAspectRatio={1} // Square cells (default)
panels={panels}
>
</DashboardLayoutComponent>Common Ratios:
cellAspectRatio={1} // Square (1:1)
cellAspectRatio={0.5} // Wider (2:1)
cellAspectRatio={2} // Taller (1:2)
cellAspectRatio={1.5} // Slightly tallerGrid Visualization
Show grid lines for debugging layout:
<DashboardLayoutComponent
columns={5}
showGridLines={true} // Display grid lines
panels={panels}
>
</DashboardLayoutComponent>Responsive Behavior
Media Query Configuration
Automatically adjust layout on different screen sizes:
<DashboardLayoutComponent
columns={5}
mediaQuery='max-width:768px' // Stack to single column on tablets
panels={panels}
>
</DashboardLayoutComponent>Common Breakpoints:
// Desktop
mediaQuery='max-width:1200px'
// Tablet
mediaQuery='max-width:768px'
// Mobile
mediaQuery='max-width:600px'Responsive Example
<DashboardLayoutComponent
columns={5}
mediaQuery='max-width:600px'
cellSpacing={[10, 10]}
allowDragging={true}
allowResizing={true}
panels={panels}
>
</DashboardLayoutComponent>Behavior:
- On desktop (>600px width): 5-column layout with all panels draggable/resizable
- On mobile (<600px width): Single column (1-column) layout, panels stack
State Persistence
Enable Persistence
Save and restore dashboard layout across sessions:
<DashboardLayoutComponent
id='my-dashboard' // Unique identifier required
enablePersistence={true} // Enable automatic persistence
panels={panels}
>
</DashboardLayoutComponent>Persistence Storage
Dashboard state is automatically saved to browser localStorage when:
- Panel positions change (drag/drop)
- Panels are resized
- Panels are added/removed
- Component is destroyed
Data is restored when component is recreated.
Manual Save & Restore
Programmatically save and restore layout:
// Save layout
const saveLayout = () => {
const layout = dashboardRef.current?.serialize();
localStorage.setItem('customLayout', JSON.stringify(layout));
};
// Restore layout
const restoreLayout = () => {
const savedLayout = localStorage.getItem('customLayout');
if (savedLayout) {
const layout = JSON.parse(savedLayout);
// Update component with saved layout
dashboardRef.current?.removeAll();
layout.forEach(panel => {
dashboardRef.current?.addPanel(panel);
});
}
};Full Persistence Example
const PersistentDashboard = () => {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const [panels, setPanels] = useState<PanelModel[]>(defaultPanels);
// Auto-save on layout changes
const handleChange = () => {
const layout = dashboardRef.current?.serialize();
sessionStorage.setItem('dashboardLayout', JSON.stringify(layout));
};
// Manual reset to default
const resetLayout = () => {
dashboardRef.current?.removeAll();
defaultPanels.forEach(panel => {
dashboardRef.current?.addPanel(panel);
});
};
return (
<>
<button onClick={resetLayout}>Reset to Default</button>
<DashboardLayoutComponent
ref={dashboardRef}
id='persistent-dashboard'
enablePersistence={true}
panels={panels}
change={handleChange}
>
</DashboardLayoutComponent>
</>
);
};Dragging and Moving Panels
Table of Contents
- Overview
- Enabling Drag Functionality
- Drag Events
- Collision and Pushing
- Custom Drag Handles
- Disabling Drag Operations
- Visual Feedback
- Advanced Dragging
- Best Practices
Overview
Dashboard Layout provides comprehensive dragging functionality for repositioning panels. When dragging a panel, the component automatically manages collisions, provides visual feedback, and triggers events at each stage of the operation.
Dragging Capabilities
- Drag-and-Drop: Move panels to new positions
- Collision Detection: Automatic pushing of overlapping panels
- Visual Preview: Shows where panel will be placed
- Event System: Hooks into drag lifecycle
- Custom Handles: Restrict dragging to specific elements
- Undo/Redo Support: Track layout changes
Enabling Drag Functionality
Basic Setup
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
function DraggableDashboard() {
const panels = [
{
id: 'panel-1',
header: 'Sales',
content: '<div>Sales data</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
},
{
id: 'panel-2',
header: 'Users',
content: '<div>User data</div>',
row: 0,
col: 2,
sizeX: 2,
sizeY: 2
}
];
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true} // Enable dragging
cellSpacing={[10, 10]}
/>
);
}Default Dragging Behavior
By default:
- Handle: Entire panel is draggable (including header and content)
- Visual: Placeholder shows target position
- Movement: Free movement within grid bounds
- Snap: Aligns to grid cells automatically
Drag Events
Dashboard Layout triggers three main events during dragging:
Event Sequence
User presses mouse down on panel
↓
dragStart event fires
↓
User moves mouse (dragging)
↓
drag event fires (continuously)
↓
User releases mouse
↓
dragStop event firesdragStart Event
Fires when user begins dragging a panel:
function DashboardWithDragStart() {
const handleDragStart = (args: DragStartEventArgs) => {
console.log('Drag started:', {
panelId: args.element?.id,
position: {
x: args.element?.offsetLeft,
y: args.element?.offsetTop
}
});
// Example: Prevent dragging specific panels
if (args.element?.id === 'locked-panel') {
args.cancel = true;
}
};
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStart={handleDragStart}
/>
);
}Available Properties:
element: DOM element being draggedevent: Mouse event objectcancel: Set to true to prevent drag
drag Event
Fires continuously while dragging:
function DashboardWithDrag() {
const [dragInfo, setDragInfo] = useState<string>('');
const handleDrag = (args: DragEventArgs) => {
const info = `Dragging: ${args.element?.id} - Position: (${args.event?.clientX}, ${args.event?.clientY})`;
setDragInfo(info);
};
return (
<div>
<div>{dragInfo}</div>
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
drag={handleDrag}
/>
</div>
);
}Use Cases:
- Show real-time coordinates
- Update external UI based on cursor position
- Validate target positions
- Trigger animations
dragStop Event
Fires when dragging completes:
function DashboardWithDragStop() {
const [lastDragInfo, setLastDragInfo] = useState<string>('');
const handleDragStop = (args: DragStopEventArgs) => {
const info = {
panelId: args.element?.id,
newPosition: {
row: args.newIndex?.row,
col: args.newIndex?.col
},
previousPosition: {
row: args.oldIndex?.row,
col: args.oldIndex?.col
},
moved: args.element?.id
};
setLastDragInfo(JSON.stringify(info, null, 2));
// Save layout after drag
saveLayout();
};
return (
<div>
<pre>{lastDragInfo}</pre>
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStop={handleDragStop}
/>
</div>
);
}Available Properties:
element: Panel that was draggednewIndex: New row/col positionoldIndex: Previous row/col positionevent: Mouse event object
Collision and Pushing
How Collision Works
When a panel collides with another during dragging: 1. Collision detected automatically 2. Overlapping panel(s) pushed in available directions 3. Push direction: left, right, top, or bottom (whichever is available) 4. Real-time feedback shows target positions
Example: Collision Behavior
function DashboardWithCollision() {
const panels = [
{
id: 'panel-1',
header: 'Panel 1',
content: '<div class="content">1</div>',
row: 0,
col: 0,
sizeX: 1,
sizeY: 1
},
{
id: 'panel-2',
header: 'Panel 2',
content: '<div class="content">2</div>',
row: 0,
col: 1,
sizeX: 3,
sizeY: 2
},
{
id: 'panel-3',
header: 'Panel 3',
content: '<div class="content">3</div>',
row: 0,
col: 4,
sizeX: 1,
sizeY: 3
}
];
const handleDragStop = (args: DragStopEventArgs) => {
console.log(`Panel ${args.element?.id} moved to row ${args.newIndex?.row}, col ${args.newIndex?.col}`);
// Panel 1 was dragged onto Panel 2
// Panel 2 automatically pushed to adjacent position
};
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
cellSpacing={[10, 10]}
allowDragging={true}
dragStop={handleDragStop}
/>
);
}Preventing Collision (Static Layout)
To prevent panels from being pushed, set allowFloating to false:
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
allowFloating={false} // Prevents automatic pushing
/>Custom Drag Handles
Restrict dragging to specific elements using draggableHandle property:
Pattern 1: Header-Only Dragging
function HeaderDragDashboard() {
const panels = [
{
id: 'chart-panel',
header: '<div class="panel-header">📊 Sales Chart</div>',
content: '<canvas id="chart"></canvas>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
draggableHandle='.panel-header' // Only drag from header
/>
);
}
// CSS
// .panel-header { cursor: move; user-select: none; }Pattern 2: Icon-Only Dragging
function IconDragDashboard() {
const panels = [
{
id: 'widget',
header: '<div><span class="drag-icon">≡</span> Widget Title</div>',
content: '<div>Widget content</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
}
];
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
draggableHandle='.drag-icon' // Only drag from icon
/>
);
}
// CSS
// .drag-icon { cursor: grab; font-weight: bold; padding: 8px; }
// .drag-icon:active { cursor: grabbing; }Pattern 3: Multiple Handles
function MultiHandleDashboard() {
const panels = [
{
id: 'complex-panel',
header: `
<div class="header-controls">
<span class="header-title">Panel Title</span>
<div class="drag-handle">↕</div>
</div>
`,
content: '<div>Content here</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
draggableHandle='.header-title, .drag-handle' // Multiple selectors
/>
);
}Disabling Drag Operations
Pattern 1: Disable for Specific Panels
function MixedDragDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const handleDragStart = (args: DragStartEventArgs) => {
const panelId = args.element?.id;
// List of panels that cannot be dragged
const lockedPanels = ['locked-1', 'locked-2', 'header-panel'];
if (lockedPanels.includes(panelId)) {
args.cancel = true; // Prevent drag
}
};
return (
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStart={handleDragStart}
/>
);
}Pattern 2: Disable All Dragging
function StaticDashboard() {
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={false} // Disable all dragging
/>
);
}Pattern 3: Conditional Disabling Based on User Role
function RoleBasedDragDashboard() {
const [userRole, setUserRole] = useState('viewer');
const canEditLayout = userRole === 'admin' || userRole === 'editor';
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={canEditLayout}
disabled={!canEditLayout}
/>
);
}Visual Feedback
Placeholder During Drag
The placeholder shows where the panel will be positioned:
/* Customize placeholder styling */
.e-placeholder {
border: 2px dashed #2196F3;
background: rgba(33, 150, 243, 0.1);
border-radius: 4px;
box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);
}Dragging Panel Visual State
/* Panel being dragged */
.e-panel.e-dragging {
opacity: 0.8;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
z-index: 1000;
}
/* Dragging animation */
.e-panel.e-dragging {
transition: none; /* Disable transitions during drag */
}Custom Drag Feedback
function CustomDragFeedbackDashboard() {
const [draggingPanel, setDraggingPanel] = useState<string | null>(null);
const handleDragStart = (args: DragStartEventArgs) => {
setDraggingPanel(args.element?.id || null);
};
const handleDragStop = () => {
setDraggingPanel(null);
};
return (
<div>
{draggingPanel && (
<div className="drag-indicator">
Moving: {draggingPanel}
</div>
)}
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStart={handleDragStart}
dragStop={handleDragStop}
/>
</div>
);
}Advanced Dragging
Programmatic Panel Movement
Move panels via movePanel method:
function ProgrammaticMoveDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const movePanel = (panelId: string, newRow: number, newCol: number) => {
dashboardRef.current?.movePanel(panelId, newRow, newCol);
};
const arrangeInGrid = () => {
movePanel('panel-1', 0, 0);
movePanel('panel-2', 0, 2);
movePanel('panel-3', 0, 4);
};
return (
<div>
<button onClick={arrangeInGrid}>Arrange in Grid</button>
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
/>
</div>
);
}Undo/Redo Dragging
function UndoRedoDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const [history, setHistory] = useState<any[]>([]);
const [historyIndex, setHistoryIndex] = useState(-1);
const recordState = () => {
const currentState = dashboardRef.current?.serialize();
const newHistory = history.slice(0, historyIndex + 1);
newHistory.push(currentState);
setHistory(newHistory);
setHistoryIndex(newHistory.length - 1);
};
const undo = () => {
if (historyIndex > 0) {
const newIndex = historyIndex - 1;
dashboardRef.current!.panels = history[newIndex];
setHistoryIndex(newIndex);
}
};
const redo = () => {
if (historyIndex < history.length - 1) {
const newIndex = historyIndex + 1;
dashboardRef.current!.panels = history[newIndex];
setHistoryIndex(newIndex);
}
};
return (
<div>
<button onClick={undo} disabled={historyIndex <= 0}>Undo</button>
<button onClick={redo} disabled={historyIndex >= history.length - 1}>Redo</button>
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStop={recordState}
/>
</div>
);
}Drag Constraints
Limit which areas panels can be dragged to:
function ConstrainedDragDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const handleDragStart = (args: DragStartEventArgs) => {
const panelId = args.element?.id;
// Prevent specific panels from moving to certain areas
if (panelId === 'priority-panel') {
// Can only be in top row
// Additional validation needed
}
};
const handleDragStop = (args: DragStopEventArgs) => {
const newRow = args.newIndex?.row || 0;
// Revert if moved to restricted area
if (args.element?.id === 'top-only-panel' && newRow > 0) {
// Restore to previous position
dashboardRef.current?.movePanel(
args.element.id,
args.oldIndex?.row || 0,
args.oldIndex?.col || 0
);
}
};
return (
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStop={handleDragStop}
/>
);
}Best Practices
1. Always Enable Drag Event Handlers
// ✅ RECOMMENDED: Track all drag lifecycle events
function BestPracticeDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const [dragState, setDragState] = useState('idle');
return (
<div>
<div>State: {dragState}</div>
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStart={() => setDragState('dragging')}
dragStop={() => {
setDragState('dropped');
setTimeout(() => setDragState('idle'), 500);
}}
/>
</div>
);
}2. Provide Visual Feedback
// ✅ RECOMMENDED: Clear visual indicators
.e-panel {
transition: all 0.2s ease;
}
.e-panel.e-dragging {
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
opacity: 0.9;
}
.e-placeholder {
border: 2px dashed #2196F3;
background: rgba(33, 150, 243, 0.08);
animation: pulse 1s infinite;
}3. Save After Drag Stops
// ✅ RECOMMENDED: Persist layout after drag
function PersistentDragDashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const handleDragStop = () => {
const layout = dashboardRef.current?.serialize();
localStorage.setItem('dashboard-layout', JSON.stringify(layout));
};
return (
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
dragStop={handleDragStop}
/>
);
}4. Use Proper Drag Handles for Content-Heavy Panels
// ✅ RECOMMENDED: For complex panels, limit drag area
function HandleDashboard() {
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
draggableHandle='.e-panel-header' // Only header is draggable
/>
);
}5. Test Keyboard Accessibility
// ✅ RECOMMENDED: Ensure keyboard support
function AccessibleDragDashboard() {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
// Cancel ongoing drag
}
};
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
allowDragging={true}
onKeyDown={handleKeyDown}
/>
);
}Dashboard Layout Events Reference
Table of Contents
Overview
Dashboard Layout provides comprehensive events for monitoring component lifecycle, user interactions, and panel changes. Events are triggered at specific moments during drag, resize, add/remove operations, and layout changes.
Lifecycle Events
created
Triggers when the Dashboard Layout component is fully created and initialized.
Event Type: EmitType<Object>
Example:
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
<DashboardLayoutComponent
panels={panels}
created={(args) => {
console.log('Dashboard Layout created successfully');
}}
>
</DashboardLayoutComponent>Use Case: Initialize dependent components or load user preferences after dashboard is ready.
destroyed
Triggers when the Dashboard Layout component is destroyed.
Event Type: EmitType<Object>
Example:
<DashboardLayoutComponent
panels={panels}
destroyed={(args) => {
console.log('Dashboard Layout destroyed');
// Cleanup resources
}}
>
</DashboardLayoutComponent>Use Case: Clean up event listeners, timers, or external resources.
Interaction Events
dragStart
Triggers when a panel is about to start being dragged. Use this event to prevent dragging certain panels or implement custom behavior.
Event Type: EmitType<DragStartArgs>
Event Arguments:
element(HTMLElement): The panel element being draggedevent(MouseEvent | TouchEvent): Original mouse or touch event
Example:
const handleDragStart = (args: DragStartArgs) => {
console.log('Drag started on element:', args.element.id);
// Prevent dragging of specific panel
if (args.element.id === 'locked-panel') {
args.cancel = true; // Cancel the drag operation
}
};
<DashboardLayoutComponent
panels={panels}
allowDragging={true}
dragStart={handleDragStart}
>
</DashboardLayoutComponent>drag
Triggers continuously while a panel is being dragged. Called on every mouse/touch movement during drag.
Event Type: EmitType<DraggedEventArgs>
Event Arguments:
element(HTMLElement): The panel element being draggedtarget(HTMLElement): The element below the dragged panelevent(MouseEvent | TouchEvent): Original mouse or touch event
Example:
const handleDrag = (args: DraggedEventArgs) => {
// Track drag progress
console.log('Dragging panel over:', args.target.id);
// Implement custom visual feedback
if (args.target) {
args.target.style.backgroundColor = 'lightblue';
}
};
<DashboardLayoutComponent
panels={panels}
allowDragging={true}
drag={handleDrag}
>
</DashboardLayoutComponent>dragStop
Triggers when a dragged panel is dropped at its final position.
Event Type: EmitType<DragStopArgs>
Event Arguments:
element(HTMLElement): The dropped panel elementtarget(HTMLElement): The element below where panel was droppedevent(MouseEvent | TouchEvent): Original mouse or touch event
Example:
const handleDragStop = (args: DragStopArgs) => {
console.log('Panel dropped successfully');
// Log final position
const panelId = args.element.id;
console.log(`Panel ${panelId} dropped on:`, args.target?.id);
// Save layout after drag
saveLayoutToStorage();
};
<DashboardLayoutComponent
panels={panels}
allowDragging={true}
dragStop={handleDragStop}
>
</DashboardLayoutComponent>resizeStart
Triggers when a panel is about to start being resized.
Event Type: EmitType<ResizeArgs>
Event Arguments:
element(HTMLElement): The panel element being resizedpanels(PanelModel[]): Model values of panels before resizeevent(MouseEvent | TouchEvent): Original mouse or touch eventisInteracted(boolean): Whether resized by user interaction
Example:
const handleResizeStart = (args: ResizeArgs) => {
console.log('Resize started on:', args.element.id);
console.log('Current panel state:', args.panels);
// Prevent resizing specific panels
if (args.element.classList.contains('fixed-size')) {
args.cancel = true;
}
};
<DashboardLayoutComponent
panels={panels}
allowResizing={true}
resizeStart={handleResizeStart}
>
</DashboardLayoutComponent>resize
Triggers continuously while a panel is being resized.
Event Type: EmitType<ResizeArgs>
Event Arguments:
element(HTMLElement): The panel being resizedpanels(PanelModel[]): Current panel configurations being modifiedevent(MouseEvent | TouchEvent): Original mouse or touch eventisInteracted(boolean): Whether resized by user interaction
Example:
const handleResize = (args: ResizeArgs) => {
// Get current panel dimensions
const width = args.element.offsetWidth;
const height = args.element.offsetHeight;
console.log(`Resizing to: ${width}px x ${height}px`);
// Track panels affected by resize
args.panels.forEach(panel => {
console.log(`Panel ${panel.id}: ${panel.sizeX} x ${panel.sizeY}`);
});
};
<DashboardLayoutComponent
panels={panels}
allowResizing={true}
resize={handleResize}
>
</DashboardLayoutComponent>resizeStop
Triggers when a panel resize is completed.
Event Type: EmitType<ResizeArgs>
Event Arguments:
element(HTMLElement): The resized panel elementpanels(PanelModel[]): Final panel configurations after resizeevent(MouseEvent | TouchEvent): Original mouse or touch eventisInteracted(boolean): Whether resized by user interaction
Example:
const handleResizeStop = (args: ResizeArgs) => {
console.log('Resize completed');
// Save final layout
const finalLayout = args.panels;
localStorage.setItem('layoutAfterResize', JSON.stringify(finalLayout));
// Notify user
console.log('Layout saved after resize');
};
<DashboardLayoutComponent
panels={panels}
allowResizing={true}
resizeStop={handleResizeStop}
>
</DashboardLayoutComponent>change
Triggers whenever panels' positions are changed (add, remove, move, drag, resize).
Event Type: EmitType<ChangeEventArgs>
Event Arguments - ChangeEventArgs:
addedPanels(PanelModel[]): Panels newly addedremovedPanels(PanelModel[]): Panels removedchangedPanels(PanelModel[]): Panels whose position changedisInteracted(boolean): True if changed by user, false if programmatic
Example:
const handleChange = (args: ChangeEventArgs) => {
console.log('Dashboard layout changed');
// Track additions
if (args.addedPanels.length > 0) {
console.log('Added panels:', args.addedPanels.map(p => p.id));
}
// Track removals
if (args.removedPanels.length > 0) {
console.log('Removed panels:', args.removedPanels.map(p => p.id));
}
// Track position changes
if (args.changedPanels.length > 0) {
console.log('Panels with position changes:', args.changedPanels);
}
// Distinguish user action from programmatic changes
if (args.isInteracted) {
console.log('Change triggered by user');
} else {
console.log('Change triggered programmatically');
}
};
<DashboardLayoutComponent
panels={panels}
change={handleChange}
>
</DashboardLayoutComponent>Event Arguments
DragStartArgs
interface DragStartArgs {
element: HTMLElement; // Panel being dragged
event: MouseEvent | TouchEvent; // Original mouse/touch event
cancel?: boolean; // Set to true to cancel drag
}DraggedEventArgs
interface DraggedEventArgs {
element: HTMLElement; // Panel being dragged
target: HTMLElement; // Element below panel
event: MouseEvent | TouchEvent; // Original mouse/touch event
}ResizeArgs
interface ResizeArgs {
element: HTMLElement; // Panel being resized
event: MouseEvent | TouchEvent; // Original mouse/touch event
panels: PanelModel[]; // Current panel configurations
isInteracted: boolean; // User-triggered or programmatic
cancel?: boolean; // Set to true to cancel resize
}ChangeEventArgs
interface ChangeEventArgs {
addedPanels: PanelModel[]; // Newly added panels
removedPanels: PanelModel[]; // Removed panels
changedPanels: PanelModel[]; // Panels with position changes
isInteracted: boolean; // User-triggered or programmatic
}Common Event Patterns
Complete Event Handling Setup
import { useRef } from 'react';
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
import {
DragStartArgs,
DragStopArgs,
ResizeArgs,
ChangeEventArgs
} from '@syncfusion/ej2-react-layouts';
export const FullEventDashboard = () => {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const handleDragStart = (args: DragStartArgs) => {
console.log('Drag start:', args.element.id);
};
const handleDrag = (args: DraggedEventArgs) => {
console.log('Dragging over:', args.target?.id);
};
const handleDragStop = (args: DragStopArgs) => {
console.log('Drag stop:', args.element.id);
saveDashboardState();
};
const handleResizeStart = (args: ResizeArgs) => {
console.log('Resize start:', args.element.id);
};
const handleResize = (args: ResizeArgs) => {
console.log('Resizing, panels changed:', args.panels.length);
};
const handleResizeStop = (args: ResizeArgs) => {
console.log('Resize stop:', args.element.id);
saveDashboardState();
};
const handleChange = (args: ChangeEventArgs) => {
if (args.addedPanels.length) console.log('Added:', args.addedPanels);
if (args.removedPanels.length) console.log('Removed:', args.removedPanels);
if (args.changedPanels.length) console.log('Changed:', args.changedPanels);
};
const saveDashboardState = () => {
const layout = dashboardRef.current?.serialize();
localStorage.setItem('dashboardState', JSON.stringify(layout));
};
return (
<DashboardLayoutComponent
ref={dashboardRef}
columns={5}
panels={panels}
allowDragging={true}
allowResizing={true}
dragStart={handleDragStart}
drag={handleDrag}
dragStop={handleDragStop}
resizeStart={handleResizeStart}
resize={handleResize}
resizeStop={handleResizeStop}
change={handleChange}
created={() => console.log('Dashboard created')}
destroyed={() => console.log('Dashboard destroyed')}
>
</DashboardLayoutComponent>
);
};Audit Trail and Logging
const auditLog: any[] = [];
const handleChange = (args: ChangeEventArgs) => {
const timestamp = new Date().toISOString();
if (args.addedPanels.length > 0) {
auditLog.push({
timestamp,
action: 'PANEL_ADDED',
panels: args.addedPanels.map(p => p.id),
isUserAction: args.isInteracted
});
}
if (args.removedPanels.length > 0) {
auditLog.push({
timestamp,
action: 'PANEL_REMOVED',
panels: args.removedPanels.map(p => p.id),
isUserAction: args.isInteracted
});
}
if (args.changedPanels.length > 0) {
auditLog.push({
timestamp,
action: 'LAYOUT_CHANGED',
panelCount: args.changedPanels.length,
isUserAction: args.isInteracted
});
}
};Error Prevention and Validation
const handleDragStart = (args: DragStartArgs) => {
const panelId = args.element.id;
// Prevent dragging locked panels
if (lockedPanels.includes(panelId)) {
args.cancel = true;
showNotification('This panel cannot be moved');
}
// Prevent dragging during data load
if (isLoading) {
args.cancel = true;
showNotification('Wait for data to load first');
}
};
const handleResizeStart = (args: ResizeArgs) => {
const panelId = args.element.id;
// Prevent resizing panels below minimum content size
if (requiredMinSize[panelId] && args.panels) {
const panel = args.panels.find(p => p.id === panelId);
if (panel && panel.sizeX < requiredMinSize[panelId]) {
args.cancel = true;
showNotification('Panel size is too small for its content');
}
}
};Getting Started with Dashboard Layout
Table of Contents
- Installation
- Setup & Configuration
- Basic Implementation
- Adding Panels
- First Dashboard
- Common Setup Issues
Installation
Dependencies
The Dashboard Layout component requires the following packages:
@syncfusion/ej2-react-layouts
├── @syncfusion/ej2-react-base
│ ├── @syncfusion/ej2-base
│ └── react (>=16.8)
└── @syncfusion/ej2-layoutsPackage Installation
Install the Syncfusion React layouts package using npm:
npm install @syncfusion/ej2-react-layouts --saveOr using yarn:
yarn add @syncfusion/ej2-react-layoutsSetup & Configuration
Import Dashboard Layout Component
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';Import CSS Styles
Add the required CSS imports to your main component or App.tsx file:
// Import base theme (choose one)
import '@syncfusion/ej2-base/styles/tailwind3.css';
// OR
// import '@syncfusion/ej2-base/styles/bootstrap5.css';
// import '@syncfusion/ej2-base/styles/fluent2.css';
// import '@syncfusion/ej2-base/styles/material3.css';
// Import Dashboard Layout styles
import '@syncfusion/ej2-react-layouts/styles/tailwind3.css';
// OR
// import '@syncfusion/ej2-react-layouts/styles/bootstrap5.css';
// import '@syncfusion/ej2-react-layouts/styles/fluent2.css';
// import '@syncfusion/ej2-react-layouts/styles/material3.css';Note: Choose only one theme. Using multiple themes simultaneously may cause styling conflicts.
Theme Options
- tailwind3 - Tailwind CSS 3 theme (recommended)
- bootstrap5 - Bootstrap 5 theme
- fluent2 - Microsoft Fluent 2 theme
- material3 - Material Design 3 theme
Using CSS Resource Generator (CRG)
For optimal bundle size, use Syncfusion's Custom Resource Generator to include only required component styles:
1. Visit: url 2. Select React platform 3. Select Dashboard Layout component 4. Choose your theme 5. Download the combined CSS file 6. Import the generated CSS file in your project
Basic Implementation
Minimal Dashboard Layout
Create your first Dashboard Layout with default settings:
import React from 'react';
import { DashboardLayoutComponent, PanelDirective, PanelsDirective } from '@syncfusion/ej2-react-layouts';
import '@syncfusion/ej2-base/styles/tailwind3.css';
import '@syncfusion/ej2-react-layouts/styles/tailwind3.css';
function App() {
return (
<DashboardLayoutComponent
id='default_dashboard'
columns={5}
>
</DashboardLayoutComponent>
);
}
export default App;Dashboard Layout with Panels
Add panels to your dashboard using the panels property:
import React from 'react';
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
function App() {
const panels = [
{
id: 'panel1',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1,
header: 'Panel 1',
content: 'Welcome to Syncfusion React Dashboard Layout'
},
{
id: 'panel2',
row: 0,
col: 2,
sizeX: 2,
sizeY: 1,
header: 'Panel 2',
content: 'This is panel 2'
}
];
return (
<DashboardLayoutComponent
id='default_dashboard'
columns={5}
panels={panels}
>
</DashboardLayoutComponent>
);
}
export default App;Adding Panels
Static Panels (Defined Upfront)
Define all panels in the panels array:
const staticPanels = [
{
id: 'analytics',
header: 'Analytics',
content: '<div>Analytics dashboard</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
},
{
id: 'metrics',
header: 'Key Metrics',
content: '<div>Key metrics panel</div>',
row: 0,
col: 2,
sizeX: 2,
sizeY: 2
},
{
id: 'reports',
header: 'Reports',
content: '<div>Reports panel</div>',
row: 2,
col: 0,
sizeX: 4,
sizeY: 1
}
];
<DashboardLayoutComponent columns={5} panels={staticPanels}>
</DashboardLayoutComponent>Dynamic Panels (HTML Attribute Method)
Define panels directly in HTML using data attributes:
<DashboardLayoutComponent id='layout' columns={5} cellSpacing={[5, 5]}>
<div id='panel1' className='e-panel' data-row='0' data-col='0' data-sizex='2' data-sizey='1'>
<div className='e-panel-header'>
Panel 1
<span className='e-icons e-close' />
</div>
<div className='e-panel-content'>
Content for panel 1
</div>
</div>
<div id='panel2' className='e-panel' data-row='0' data-col='2' data-sizex='2' data-sizey='1'>
<div className='e-panel-header'>
Panel 2
<span className='e-icons e-close' />
</div>
<div className='e-panel-content'>
Content for panel 2
</div>
</div>
</DashboardLayoutComponent>First Dashboard
Complete Functional Dashboard Example
import React, { useRef, useState } from 'react';
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
import '@syncfusion/ej2-base/styles/tailwind3.css';
import '@syncfusion/ej2-react-layouts/styles/tailwind3.css';
function Dashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const [panels] = useState([
{
id: 'panel1',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2,
header: 'Sales Overview',
content: (
<div style={{ padding: '20px' }}>
<h3>Total Sales: $150,000</h3>
<p>This month's revenue</p>
</div>
)
},
{
id: 'panel2',
row: 0,
col: 2,
sizeX: 2,
sizeY: 2,
header: 'User Activity',
content: (
<div style={{ padding: '20px' }}>
<h3>Active Users: 2,450</h3>
<p>Currently online</p>
</div>
)
},
{
id: 'panel3',
row: 2,
col: 0,
sizeX: 3,
sizeY: 1,
header: 'Performance Metrics',
content: (
<div style={{ padding: '20px' }}>
<p>System uptime: 99.9%</p>
<p>Response time: 45ms</p>
</div>
)
}
]);
const handleDragStop = () => {
console.log('Panel rearranged');
};
const handleChange = (args: any) => {
console.log('Layout changed:', args);
};
return (
<div style={{ padding: '20px' }}>
<h1>My Dashboard</h1>
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
columns={5}
cellSpacing={[10, 10]}
panels={panels}
allowDragging={true}
dragStop={handleDragStop}
change={handleChange}
>
</DashboardLayoutComponent>
</div>
);
}
export default Dashboard;Dashboard with Customization
<DashboardLayoutComponent
id='custom_dashboard'
columns={5}
cellSpacing={[15, 15]}
cellAspectRatio={1.5}
allowDragging={true}
allowResizing={true}
allowFloating={true}
enablePersistence={true}
showGridLines={false}
mediaQuery='max-width:600px'
panels={panels}
>
</DashboardLayoutComponent>Common Setup Issues
Issue: Styles Not Applying
Problem: Dashboard Layout appears without any styling.
Solution: Ensure CSS imports are in correct order:
// Base styles first
import '@syncfusion/ej2-base/styles/tailwind3.css';
// Then component styles
import '@syncfusion/ej2-react-layouts/styles/tailwind3.css';Issue: Panels Not Displaying
Problem: Panel array is defined but panels don't render.
Solution: Verify panel configuration:
// Ensure panels have required properties
const panels = [
{
id: 'unique-id', // Required: Unique identifier
row: 0, // Required: Row position
col: 0, // Required: Column position
sizeX: 2, // Optional: Width (default: 1)
sizeY: 1, // Optional: Height (default: 1)
header: 'Title', // Optional: Panel header
content: 'Content' // Optional: Panel content
}
];Issue: "Cannot read property 'current' of undefined"
Problem: useRef is undefined or ref is not properly attached.
Solution: Properly import and use useRef:
import { useRef } from 'react';
const dashboardRef = useRef<DashboardLayoutComponent>(null);
<DashboardLayoutComponent ref={dashboardRef} panels={panels}>
</DashboardLayoutComponent>Issue: TypeScript Errors
Problem: TypeScript complains about PanelModel or component types.
Solution: Import types explicitly:
import {
DashboardLayoutComponent,
PanelModel
} from '@syncfusion/ej2-react-layouts';
const panels: PanelModel[] = [...];Issue: Layout Breaks on Mobile
Problem: Dashboard doesn't respond well on small screens.
Solution: Use mediaQuery property:
<DashboardLayoutComponent
columns={5}
mediaQuery='max-width:768px' // Stacks to single column on tablets
panels={panels}
>
</DashboardLayoutComponent>Dashboard Layout Methods Reference
Table of Contents
- Overview
- Panel Management Methods
- Layout Manipulation Methods
- Serialization Methods
- Utility Methods
- Common Method Patterns
Overview
Dashboard Layout provides comprehensive methods for programmatic control over panels and layout. These methods enable dynamic panel management, position manipulation, and state serialization.
Panel Management Methods
addPanel
Allows to add a new panel to the DashboardLayout dynamically.
Signature:
addPanel(panel: PanelModel): voidParameters:
panel(PanelModel): Defines the panel object with required properties.
Example:
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const handleAddPanel = () => {
const newPanel: PanelModel = {
id: 'panel-new',
header: 'New Panel',
content: 'Panel content here',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
};
dashboardRef.current?.addPanel(newPanel);
};
<DashboardLayoutComponent
ref={dashboardRef}
panels={panels}
>
</DashboardLayoutComponent>removePanel
Removes a specific panel from the DashboardLayout by its ID.
Signature:
removePanel(id: string): voidParameters:
id(string): The panel ID to be removed.
Example:
const handleRemovePanel = () => {
dashboardRef.current?.removePanel('panel1');
};removeAll
Removes all panels from the DashboardLayout at once.
Signature:
removeAll(): voidExample:
const handleRemoveAllPanels = () => {
dashboardRef.current?.removeAll();
};updatePanel
Allows to update an existing panel in the DashboardLayout.
Signature:
updatePanel(panel: PanelModel): voidParameters:
panel(PanelModel): The updated panel object with the same ID.
Example:
const handleUpdatePanel = () => {
const updatedPanel: PanelModel = {
id: 'panel1',
header: 'Updated Header',
content: 'Updated content',
row: 0,
col: 0,
sizeX: 3,
sizeY: 2
};
dashboardRef.current?.updatePanel(updatedPanel);
};Layout Manipulation Methods
movePanel
Moves a panel to a new position in the DashboardLayout specified by row and column.
Signature:
movePanel(id: string, row: number, col: number): voidParameters:
id(string): The panel ID to move.row(number): Target row position.col(number): Target column position.
Example:
const handleMovePanel = () => {
// Move panel1 to row 1, column 2
dashboardRef.current?.movePanel('panel1', 1, 2);
};Use Case: Programmatically rearrange panels based on user preferences or logic.
resizePanel
Resizes a specific panel to new dimensions.
Signature:
resizePanel(id: string, sizeX: number, sizeY: number): voidParameters:
id(string): The panel ID to resize.sizeX(number): New width in cells.sizeY(number): New height in cells.
Example:
const handleResizePanel = () => {
// Resize panel1 to 3 columns x 2 rows
dashboardRef.current?.resizePanel('panel1', 3, 2);
};Use Case: Automatically adjust panel sizes based on content or user actions.
Serialization Methods
serialize
Returns the current panels configuration as a PanelModel[] array. Useful for saving layout state.
Signature:
serialize(): PanelModel[]Returns: Array of PanelModel objects representing current panel configuration.
Example:
const handleSaveLayout = () => {
const currentLayout = dashboardRef.current?.serialize();
// Save to localStorage
localStorage.setItem('dashboardLayout', JSON.stringify(currentLayout));
// Or send to server
fetch('/api/save-layout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(currentLayout)
});
};Use Case: Persist user's custom dashboard layout for later restoration.
Utility Methods
refreshDraggableHandle
Updates the draggable handle when draggable panel elements are bound dynamically. Use this when you update the draggable handle CSS selector or dynamically add/remove draggable elements.
Signature:
refreshDraggableHandle(): voidExample:
const handleAddDraggableElements = () => {
// After adding new draggable elements to panels
dashboardRef.current?.refreshDraggableHandle();
};Use Case: Update draggable handles after dynamic template changes.
destroy
Destroys the DashboardLayout component and removes event listeners.
Signature:
destroy(): voidExample:
useEffect(() => {
return () => {
// Cleanup on unmount
dashboardRef.current?.destroy();
};
}, []);Common Method Patterns
Complete Panel Lifecycle Management
import { useRef } from 'react';
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
export const DashboardManager = () => {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
// Add new panel
const addNewPanel = () => {
dashboardRef.current?.addPanel({
id: `panel-${Date.now()}`,
header: 'New Dashboard Item',
content: '<div>Content here</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
});
};
// Remove specific panel
const removeSpecificPanel = (panelId: string) => {
dashboardRef.current?.removePanel(panelId);
};
// Update panel header
const updatePanelHeader = (panelId: string, newHeader: string) => {
const panels = dashboardRef.current?.serialize();
const panelToUpdate = panels?.find(p => p.id === panelId);
if (panelToUpdate) {
panelToUpdate.header = newHeader;
dashboardRef.current?.updatePanel(panelToUpdate);
}
};
// Rearrange panels
const rearrangePanels = () => {
dashboardRef.current?.movePanel('panel1', 1, 2);
dashboardRef.current?.movePanel('panel2', 0, 0);
};
// Save layout to storage
const saveLayout = () => {
const layout = dashboardRef.current?.serialize();
localStorage.setItem('myDashboard', JSON.stringify(layout));
};
return (
<div>
<button onClick={addNewPanel}>Add Panel</button>
<button onClick={() => removeSpecificPanel('panel1')}>Remove Panel 1</button>
<button onClick={rearrangePanels}>Rearrange</button>
<button onClick={saveLayout}>Save Layout</button>
<DashboardLayoutComponent
ref={dashboardRef}
columns={5}
panels={initialPanels}
>
</DashboardLayoutComponent>
</div>
);
};Layout Save and Restore
// Save current layout
const saveCurrentLayout = () => {
const layout = dashboardRef.current?.serialize();
const layoutJSON = JSON.stringify(layout, null, 2);
// Download as file or save to DB
const blob = new Blob([layoutJSON], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'dashboard-layout.json';
link.click();
};
// Restore layout from saved state
const restoreLayout = (layoutJSON: string) => {
const layout = JSON.parse(layoutJSON);
dashboardRef.current?.removeAll();
layout.forEach((panelConfig: PanelModel) => {
dashboardRef.current?.addPanel(panelConfig);
});
};Programmatic Layout Adjustment
// Maximize a panel
const maximizePanel = (panelId: string) => {
dashboardRef.current?.resizePanel(panelId, 5, 3);
dashboardRef.current?.movePanel(panelId, 0, 0);
};
// Reset all panels to default size
const resetAllPanels = () => {
const panels = dashboardRef.current?.serialize();
panels?.forEach(panel => {
dashboardRef.current?.resizePanel(panel.id, 2, 1);
});
};Panel Templates and Content
Table of Contents
- Overview
- Panel Structure
- Header Templates
- Content Templates
- JSX Content Rendering
- Embedding Syncfusion Components
- Dynamic Content Updates
- Template Best Practices
Overview
Dashboard Layout panels support flexible content rendering through HTML strings, JSX components, and Syncfusion widgets. Panel templates define the visual structure and content that appears within each panel, including headers (optional), content area, and custom styling.
Panel Composition
Each panel consists of:
- Header (optional): Title bar with custom content
- Content Area: Main panel body containing data or widgets
- Panel Wrapper: Container managing styling and interactions
Template Types Supported
| Type | Usage | Performance |
|---|---|---|
| HTML String | Simple static content | ⚡ Fastest |
| JSX Component | Dynamic React components | 🔄 Moderate |
| Syncfusion Control | Charts, Grids, DataGrids | 📊 Feature-rich |
| Mixed Content | Combination of above | 🎨 Most flexible |
Panel Structure
Basic Panel Model with Templates
interface PanelModel {
id?: string; // Unique panel identifier
header?: string; // Header text or HTML
content?: string | JSX.Element; // Panel content
row?: number; // Row position (0-based)
col?: number; // Column position (0-based)
sizeX?: number; // Width in grid cells
sizeY?: number; // Height in grid cells
minSizeX?: number; // Minimum width
minSizeY?: number; // Minimum height
maxSizeX?: number; // Maximum width
maxSizeY?: number; // Maximum height
}Creating Panels with Content
const panels = [
{
id: 'panel-1',
header: 'Sales Report',
content: '<div class="content">Revenue: $50,000</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];Header Templates
Headers provide visual identification and optional interactive controls for panels.
Simple Text Headers
const panels = [
{
id: 'dashboard-stats',
header: 'Dashboard Statistics',
content: '<div class="stats">Stats content here</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
<DashboardLayoutComponent panels={panels} />HTML Headers with Custom Styling
const panels = [
{
id: 'chart-panel',
header: '<div class="custom-header"><span>📊 Sales Chart</span></div>',
content: '<div class="chart-content">Chart here</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];Headers with Interactive Elements
const panels = [
{
id: 'interactive-panel',
header: `
<div class="header-container">
<span class="title">Panel Title</span>
<button class="refresh-btn" onclick="refreshData()">🔄</button>
</div>
`,
content: '<div id="panel-content">Content here</div>',
row: 0,
col: 0,
sizeX: 3,
sizeY: 2
}
];Header Properties via PanelModel
The header property accepts:
- String: Simple text (e.g., "Panel Title")
- HTML: Full HTML markup with styling
- Custom Content: Interactive elements and icons
Best Practices:
- Keep headers concise (single line recommended)
- Use consistent icon sets (emoji or icon libraries)
- Avoid complex nested structures in headers
Content Templates
Content templates define the main panel body, supporting various rendering approaches.
HTML String Content
The simplest approach for static or data-driven content:
const panels = [
{
id: 'stats-panel',
header: 'System Statistics',
content: `
<div class="stats-wrapper">
<div class="stat-item">
<label>CPU Usage</label>
<span class="value">45%</span>
</div>
<div class="stat-item">
<label>Memory Usage</label>
<span class="value">62%</span>
</div>
<div class="stat-item">
<label>Disk Usage</label>
<span class="value">78%</span>
</div>
</div>
`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
}
];Dynamic Content with Data Binding
function Dashboard() {
const [stats, setStats] = useState({ cpu: 45, memory: 62, disk: 78 });
const panels = [
{
id: 'stats-panel',
header: 'System Stats',
content: `
<div class="stats-container">
<p>CPU: ${stats.cpu}%</p>
<p>Memory: ${stats.memory}%</p>
<p>Disk: ${stats.disk}%</p>
</div>
`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
}
];
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
/>
);
}JSX Content Rendering
For complex React components, render JSX-based content:
function Dashboard() {
const [data, setData] = useState([]);
// Custom React component for panel content
const PanelContent = ({ title, items }) => (
<div className="panel-content">
<h3>{title}</h3>
<ul>
{items.map((item, idx) => (
<li key={idx}>{item}</li>
))}
</ul>
</div>
);
// Convert to HTML string
const contentHtml = ReactDOM.renderToString(
<PanelContent
title="Recent Items"
items={['Item 1', 'Item 2', 'Item 3']}
/>
);
const panels = [
{
id: 'content-panel',
header: 'Content List',
content: contentHtml,
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
/>
);
}Embedding Syncfusion Components
Dashboard Layout panels can host any Syncfusion component (Charts, Grids, etc.):
Embedding a Chart
import { ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject, Legend, Tooltip, LineSeries } from '@syncfusion/ej2-react-charts';
import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
function DashboardWithChart() {
const chartContent = `
<div id="chart-container" style="height: 100%; width: 100%;"></div>
`;
const panels = [
{
id: 'chart-panel',
header: 'Revenue Trend',
content: chartContent,
row: 0,
col: 0,
sizeX: 3,
sizeY: 2
}
];
React.useEffect(() => {
const data = [
{ x: 'Jan', y: 25 },
{ x: 'Feb', y: 30 },
{ x: 'Mar', y: 28 }
];
// Mount chart to container after panel renders
const root = ReactDOM.createRoot(document.getElementById('chart-container'));
root.render(
<ChartComponent id='line-chart' title='Sales Trend' tooltip={{ enable: true }}>
<Inject services={[Legend, Tooltip, LineSeries]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={data} xName='x' yName='y' type='Line' />
</SeriesCollectionDirective>
</ChartComponent>
);
}, []);
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
cellSpacing={[10, 10]}
/>
);
}Embedding a DataGrid
import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Page, Search, Toolbar } from '@syncfusion/ej2-react-grids';
function DashboardWithGrid() {
const gridContent = `
<div id="grid-container" style="height: 100%; width: 100%;"></div>
`;
const panels = [
{
id: 'grid-panel',
header: 'Data Table',
content: gridContent,
row: 0,
col: 0,
sizeX: 4,
sizeY: 3
}
];
React.useEffect(() => {
const data = [
{ id: 1, name: 'John', dept: 'Sales', salary: 50000 },
{ id: 2, name: 'Jane', dept: 'HR', salary: 48000 },
{ id: 3, name: 'Bob', dept: 'IT', salary: 65000 }
];
const root = ReactDOM.createRoot(document.getElementById('grid-container'));
root.render(
<GridComponent dataSource={data} allowPaging={true}>
<ColumnsDirective>
<ColumnDirective field='id' headerText='ID' width='80' />
<ColumnDirective field='name' headerText='Name' width='100' />
<ColumnDirective field='dept' headerText='Department' width='100' />
<ColumnDirective field='salary' headerText='Salary' width='100' />
</ColumnsDirective>
<Inject services={[Page, Search, Toolbar]} />
</GridComponent>
);
}, []);
return (
<DashboardLayoutComponent
id='dashboard'
panels={panels}
columns={5}
cellSpacing={[10, 10]}
/>
);
}Dynamic Content Updates
Update panel content after creation using updatePanel method:
function Dashboard() {
const dashboardRef = useRef<DashboardLayoutComponent>(null);
const [counter, setCounter] = useState(0);
const panels = [
{
id: 'counter-panel',
header: 'Counter',
content: `<div class="counter">Count: ${counter}</div>`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
}
];
const updateContent = () => {
const newCount = counter + 1;
setCounter(newCount);
// Update panel content programmatically
const updatedPanel = {
id: 'counter-panel',
header: 'Counter',
content: `<div class="counter">Count: ${newCount}</div>`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
};
dashboardRef.current?.updatePanel(updatedPanel);
};
return (
<div>
<button onClick={updateContent}>Increment</button>
<DashboardLayoutComponent
ref={dashboardRef}
id='dashboard'
panels={panels}
columns={5}
/>
</div>
);
}Template Best Practices
1. Performance Optimization
// ❌ AVOID: Recreating panels on every render
function BadDashboard() {
const panels = [
{
id: 'panel1',
content: '<div>Content</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
}
];
return <DashboardLayoutComponent panels={panels} />;
}
// ✅ RECOMMENDED: Memoize panel definitions
function GoodDashboard() {
const panels = useMemo(() => [
{
id: 'panel1',
content: '<div>Content</div>',
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
}
], []);
return <DashboardLayoutComponent panels={panels} />;
}2. Content Sizing
// ✅ RECOMMENDED: Content with proper height
const panels = [
{
id: 'chart-panel',
header: 'Chart',
content: '<div style="height: 100%; overflow: auto;">Content</div>',
row: 0,
col: 0,
sizeX: 3,
sizeY: 3
}
];3. Error Handling
function SafeDashboard() {
const panels = [
{
id: 'safe-panel',
header: 'Data Panel',
content: (() => {
try {
return generateComplexContent();
} catch (e) {
return `<div class="error">Error: ${e.message}</div>`;
}
})(),
row: 0,
col: 0,
sizeX: 2,
sizeY: 2
}
];
return <DashboardLayoutComponent panels={panels} />;
}4. Accessibility
// ✅ RECOMMENDED: Semantic HTML with ARIA labels
const panels = [
{
id: 'accessible-panel',
header: `<div role="heading" aria-level="2">Sales Data</div>`,
content: `
<div role="main" aria-label="Sales information">
<table aria-label="Sales table">
<tr><td>Q1</td><td>$50,000</td></tr>
<tr><td>Q2</td><td>$55,000</td></tr>
</table>
</div>
`,
row: 0,
col: 0,
sizeX: 3,
sizeY: 2
}
];5. Mobile Responsiveness
function ResponsiveDashboard() {
const isMobile = window.innerWidth < 768;
const panels = [
{
id: 'responsive-panel',
header: 'Dashboard',
content: '<div>Content adapts to screen size</div>',
row: 0,
col: 0,
sizeX: isMobile ? 1 : 2,
sizeY: isMobile ? 1 : 2
}
];
return (
<DashboardLayoutComponent
panels={panels}
columns={isMobile ? 2 : 5}
mediaQuery="max-width: 768px"
/>
);
}6. Styling Content
// ✅ RECOMMENDED: Use CSS classes for maintainability
const panels = [
{
id: 'styled-panel',
header: 'Styled Panel',
content: `
<div class="panel-content">
<div class="metric">
<span class="label">Revenue</span>
<span class="value">$100,000</span>
</div>
</div>
`,
row: 0,
col: 0,
sizeX: 2,
sizeY: 1
}
];
// CSS
// .panel-content { padding: 20px; }
// .metric { display: flex; justify-content: space-between; }
// .value { font-weight: bold; color: #2196F3; }