> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/wazuh/wazuh-dashboard-plugins/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating Custom Dashboards

> Learn how to create and customize dashboards in Wazuh Dashboard to visualize security data according to your requirements

The Wazuh Dashboard provides a flexible framework for creating custom dashboards that visualize security data according to your specific monitoring requirements. Custom dashboards enable you to aggregate metrics, charts, and visualizations to provide tailored views of your security posture.

## Overview

Custom dashboards in Wazuh Dashboard are composed of panels that display visualizations, each querying security event data from the Wazuh indices. The dashboard framework supports:

* **Dynamic panel layouts** with customizable grid positioning
* **Multiple visualization types** including line charts, pie charts, histograms, and area charts
* **Real-time data queries** using the OpenSearch query language
* **Interactive filtering** and time range selection
* **Agent-specific views** for pinned agent monitoring

## Dashboard architecture

Custom dashboards are built using three core components:

### Dashboard configuration

The `DashboardConfig` class defines the dashboard metadata and orchestrates panel generation:

```typescript theme={null}
export class CustomDashboardConfig extends DashboardConfig {
  constructor(indexPatternId: string) {
    super(
      indexPatternId,
      new CustomDashboardLayoutDefinition(indexPatternId)
    );
  }

  protected override getId(): string {
    return 'custom-security-dashboard-tab';
  }

  protected override getTitle(): string {
    return 'Custom Security Dashboard';
  }

  protected override getDescription(): string {
    return 'Custom security metrics and visualizations';
  }
}
```

Reference: `plugins/main/common/dashboards/lib/dashboard-config-service.ts:77`

### Layout definition

The `DashboardLayoutDefinition` class specifies the grid layout and visualization placement:

```typescript theme={null}
export class CustomDashboardLayoutDefinition extends DashboardLayoutDefinition {
  constructor(indexPatternId: string) {
    super();
    this.setGridVisualizationPairs(
      {
        gridData: {
          w: 36,  // Width (48 units total)
          h: 12,  // Height
          x: 0,   // X position
          y: 0,   // Y position
        },
        savedVis: getVisStateAlertsTimeline(indexPatternId),
      },
      {
        gridData: {
          w: 12,
          h: 12,
          x: 36,
          y: 0,
        },
        savedVis: getVisStateTopAlertTypes(indexPatternId),
      }
    );
  }
}
```

Reference: `plugins/main/common/dashboards/dashboard-definitions/overview/mitre/overview/dashboard.ts:13`

### Visualization definitions

Visualization definitions specify the chart type, data aggregations, and query filters:

```typescript theme={null}
const getVisStateAlertsTimeline = (indexPatternId: string) => {
  return {
    id: 'Custom-Alerts-Timeline',
    title: 'Security alerts over time',
    type: 'line',
    params: {
      type: 'line',
      addTooltip: true,
      addLegend: true,
      legendPosition: 'right',
      categoryAxes: [
        {
          id: 'CategoryAxis-1',
          type: 'category',
          position: 'bottom',
          show: true,
          labels: { show: true, filter: true },
        },
      ],
      valueAxes: [
        {
          id: 'ValueAxis-1',
          name: 'LeftAxis-1',
          type: 'value',
          position: 'left',
          title: { text: 'Count' },
        },
      ],
    },
    data: {
      searchSource: {
        query: {
          language: 'kuery',
          query: '',
        },
        filter: [],
        index: indexPatternId,
      },
      aggs: [
        {
          id: '1',
          enabled: true,
          type: 'count',
          schema: 'metric',
          params: {},
        },
        {
          id: '2',
          enabled: true,
          type: 'date_histogram',
          schema: 'segment',
          params: {
            field: 'timestamp',
            timeRange: { from: 'now-7d', to: 'now' },
            interval: 'auto',
          },
        },
      ],
    },
  };
};
```

Reference: `plugins/main/public/components/overview/mitre/dashboard/dashboard-panels.ts:4`

## Creating a custom dashboard

### Step 1: Define visualization states

Create visualization configuration functions for each panel:

```typescript theme={null}
// File: custom-dashboard-panels.ts

const getVisStateSecurityEvents = (indexPatternId: string) => {
  return {
    id: 'Custom-Security-Events',
    title: 'Security events by severity',
    type: 'pie',
    params: {
      type: 'pie',
      addTooltip: true,
      addLegend: true,
      legendPosition: 'right',
      isDonut: true,
    },
    data: {
      searchSource: {
        query: { language: 'kuery', query: '' },
        filter: [],
        index: indexPatternId,
      },
      aggs: [
        {
          id: '1',
          type: 'count',
          schema: 'metric',
        },
        {
          id: '2',
          type: 'terms',
          schema: 'segment',
          params: {
            field: 'rule.level',
            orderBy: '1',
            order: 'desc',
            size: 10,
          },
        },
      ],
    },
  };
};
```

### Step 2: Create layout definition

Define the dashboard grid layout:

```typescript theme={null}
// File: custom-dashboard-layout.ts

import { DashboardLayoutDefinition } from '../lib/dashboard-config-service';
import { getVisStateSecurityEvents } from './custom-dashboard-panels';

export class CustomDashboardLayoutDefinition extends DashboardLayoutDefinition {
  constructor(indexPatternId: string) {
    super();
    this.setGridVisualizationPairs(
      {
        gridData: { w: 24, h: 14, x: 0, y: 0 },
        savedVis: getVisStateSecurityEvents(indexPatternId),
      }
      // Add additional panels here
    );
  }
}
```

### Step 3: Create dashboard configuration

Implement the dashboard configuration class:

```typescript theme={null}
// File: custom-dashboard-config.ts

import { DashboardConfig } from '../lib/dashboard-config-service';
import { CustomDashboardLayoutDefinition } from './custom-dashboard-layout';

export class CustomDashboardConfig extends DashboardConfig {
  constructor(indexPatternId: string) {
    super(
      indexPatternId,
      new CustomDashboardLayoutDefinition(indexPatternId)
    );
  }

  protected override getId(): string {
    return 'custom-security-dashboard';
  }

  protected override getTitle(): string {
    return 'Custom Security Dashboard';
  }

  protected override getDescription(): string {
    return 'Customized security monitoring dashboard';
  }
}
```

### Step 4: Register the dashboard component

Create the React component using the `createDashboard` helper:

```typescript theme={null}
// File: dashboard.tsx

import { createDashboard } from '../../../common/dashboards';
import { DashboardDataSource } from '../data-source';
import { DataSourceRepository } from '../repository';

export const CustomSecurityDashboard = createDashboard({
  DataSource: DashboardDataSource,
  DataSourceRepositoryCreator: DataSourceRepository,
  getDashboardPanels: [
    {
      dashboardId: 'custom-security-dashboard',
      className: 'custom-dashboard-container',
    },
  ],
});
```

Reference: `plugins/main/public/components/common/dashboards/dashboard.tsx:134`

## Visualization types

### Line chart

Temporal data visualization with trend analysis:

```typescript theme={null}
type: 'line'
params: {
  seriesParams: [{
    type: 'line',
    mode: 'normal',
    drawLinesBetweenPoints: true,
    showCircles: true,
    lineWidth: 2,
  }],
}
```

### Pie chart

Proportional distribution of categorical data:

```typescript theme={null}
type: 'pie'
params: {
  isDonut: true,
  labels: { show: false, values: true },
}
```

### Histogram

Bar chart for categorical comparisons:

```typescript theme={null}
type: 'histogram'
params: {
  seriesParams: [{
    type: 'histogram',
    mode: 'stacked',
  }],
}
```

### Area chart

Stacked temporal visualizations:

```typescript theme={null}
type: 'area'
params: {
  seriesParams: [{
    type: 'histogram',
    mode: 'normal',
    interpolate: 'linear',
  }],
}
```

Reference: `plugins/main/public/components/overview/mitre/dashboard/dashboard-panels.ts:163`

## Advanced customization

### Adding custom filters

Implement managed filters for dynamic query modification:

```typescript theme={null}
export const CustomDashboard = createDashboard({
  DataSource: DashboardDataSource,
  DataSourceRepositoryCreator: DataSourceRepository,
  managedFilters: {
    severity: {
      field: 'rule.level',
      label: 'Severity level',
      options: [
        { value: '>=12', label: 'High' },
        { value: '>=7', label: 'Medium' },
        { value: '<7', label: 'Low' },
      ],
    },
  },
  getDashboardPanels: [{
    dashboardId: 'custom-dashboard-id',
  }],
});
```

### Agent-specific dashboards

Create dashboards that display data for a specific agent:

```typescript theme={null}
getDashboardPanels: [
  {
    dashboardId: 'custom-overview-dashboard',
    agentDashboardId: 'custom-agent-dashboard',
  },
]
```

The framework automatically switches between overview and agent views based on context.

### Custom aggregations

Implement complex data aggregations:

```typescript theme={null}
aggs: [
  {
    id: '1',
    type: 'count',
    schema: 'metric',
  },
  {
    id: '2',
    type: 'terms',
    schema: 'group',
    params: {
      field: 'rule.mitre.technique',
      orderBy: '1',
      order: 'desc',
      size: 5,
    },
  },
  {
    id: '3',
    type: 'terms',
    schema: 'segment',
    params: {
      field: 'rule.mitre.tactic',
      orderBy: '1',
      order: 'desc',
      size: 5,
    },
  },
]
```

Reference: `plugins/main/public/components/overview/mitre/dashboard/dashboard-panels.ts:349`

### Time range configuration

Configure default time ranges for dashboard queries:

```typescript theme={null}
params: {
  field: 'timestamp',
  timeRange: { from: 'now-30d', to: 'now' },
  interval: 'auto',
}
```

## Dashboard panel service

The `DashboardPanelBuilderService` automatically generates panel configurations from layout definitions:

```typescript theme={null}
class DashboardPanelBuilderService {
  getDashboardPanels(): DashboardByRendererPanels {
    // Iterates through grid visualization pairs
    // Assigns sequential panel IDs
    // Returns fully configured panel structure
  }
}
```

Reference: `plugins/main/common/dashboards/lib/dashboard-config-service.ts:8`

## Query language

Dashboards support both Kuery and Lucene query languages:

### Kuery syntax

```typescript theme={null}
searchSource: {
  query: {
    language: 'kuery',
    query: 'rule.level >= 12 and rule.groups: "malware"',
  },
}
```

### Lucene syntax

```typescript theme={null}
searchSource: {
  query: {
    language: 'lucene',
    query: 'rule.level:[12 TO *] AND rule.groups:"authentication_failed"',
  },
}
```

## Best practices

### Performance optimization

* Limit the number of panels per dashboard to maintain responsiveness
* Use appropriate aggregation sizes to balance detail and performance
* Configure reasonable default time ranges to avoid querying excessive data
* Implement pagination for large result sets

### Layout design

* The grid system uses 48 horizontal units
* Standard panel heights range from 12 to 16 units
* Position related visualizations adjacent to each other
* Place high-priority metrics in the top-left quadrant

### Data accuracy

* Specify appropriate time fields for temporal aggregations
* Use correct field types in aggregation parameters
* Test queries independently before integrating into dashboards
* Validate index pattern availability

### Maintainability

* Organize visualization definitions in separate files
* Use descriptive IDs and titles for panels
* Document custom aggregation logic
* Implement type safety with TypeScript interfaces

## Troubleshooting

### Dashboard not rendering

* Verify the index pattern ID is correct and accessible
* Check that all required fields exist in the index
* Confirm the dashboard ID is unique and properly registered
* Review browser console for configuration errors

### Visualizations showing no data

* Validate the time range includes relevant events
* Verify aggregation field names match index schema
* Check query syntax for errors
* Confirm data exists for the specified filters

### Layout issues

* Ensure grid coordinates do not overlap
* Verify total width does not exceed 48 units
* Check that panel heights provide adequate space for content
* Test layout on different screen resolutions

## Related resources

* MITRE ATT\&CK framework mapping for reference dashboard implementations
* Dashboard renderer service for understanding the rendering pipeline
* Data source configuration for custom data fetching logic


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.