
Dashboard
- 8 installs
- 8 repo stars
- Updated August 4, 2026
- bbeierle12/skill-mcp-claude
Dashboard is a Claude skill that routes data-dashboard development to sub-skills for charts, real-time updates, and filtering.
About
Dashboard is a Claude skill that routes data-dashboard development work to specialized sub-skills for charts, real-time updates, and filtering. It outlines a dashboard architecture with a data layer, chart components, filter panel, layout, and real-time manager. A developer uses it when building analytics applications with visualizations, live data, and faceted filters.
- Router for data-dashboard development patterns
- Sub-skills for charts, real-time streaming, and filtering
- Covers responsive grid layouts and data-flow architecture
Dashboard by the numbers
- 8 all-time installs (skills.sh)
- Ranked #1,733 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
dashboard capabilities & compatibility
- Capabilities
- dashboard scaffolding · data visualization · real time ui
- Use cases
- frontend · data analysis
What dashboard says it does
Data dashboard development patterns including charts, real-time updates, filtering, and responsive layouts for analytics applications.
Routes to specialized sub-skills for data dashboard development.
- Use CSS Grid for dashboard tiles
npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill dashboardAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 8 |
| Last updated | August 4, 2026 |
| Repository | bbeierle12/skill-mcp-claude ↗ |
What it does
Structure a data dashboard with charts, real-time streaming, and filtering.
Who is it for?
Building analytics dashboards with charts, live data, and filters.
Skip if: Backend-only or non-visualization tasks.
When should I use this skill?
Building a data dashboard with charts, real-time updates, or filtering.
What you get
A structured dashboard with charts, real-time data, and filtering.
- Dashboard architecture
- Chart components
- Filter panel
By the numbers
- 3 sub-skills (charts, real-time, filtering)
Files
Dashboard Router
Routes to specialized sub-skills for data dashboard development.
Sub-Skills
| Sub-Skill | Use When |
|---|---|
| charts | Data visualization with D3, Recharts, or Chart.js |
| real-time | WebSocket connections, live data streaming |
| filtering | Search, faceted filters, query builders |
Architecture Overview
Dashboard
├── DataLayer (fetching, caching, transforms)
├── ChartComponents (reusable visualizations)
├── FilterPanel (search, facets, date ranges)
├── Layout (responsive grid, drag-and-drop)
└── RealTimeManager (WebSocket, polling fallback)Key Patterns
Data Flow
API/WebSocket → DataStore → Transform → Charts
↑
Filters/QueriesResponsive Layout
- Use CSS Grid for dashboard tiles
- Implement breakpoint-based chart simplification
- Consider virtualization for large data tables
Common Combinations
- Analytics dashboard:
charts+filtering - Live monitoring:
charts+real-time - Full-featured: All sub-skills
{
"name": "dashboard",
"description": "Data dashboard development patterns including charts, real-time updates, filtering, and responsive layouts for analytics applications.",
"tags": [
"data-visualization",
"web",
"routing"
],
"sub_skills": [
{
"name": "charts",
"file": "references/charts.md",
"triggers": [
"chart",
"graph",
"D3",
"recharts",
"visualization"
]
},
{
"name": "real-time",
"file": "references/real-time.md",
"triggers": [
"WebSocket",
"live",
"streaming",
"real-time"
]
},
{
"name": "filtering",
"file": "references/filtering.md",
"triggers": [
"filter",
"search",
"query",
"facet"
]
}
],
"source": "claude-user",
"type": "router",
"depends_on": [],
"enhances": [
"d3js-visualization"
],
"last_reviewed_at": "2026-06-01",
"review_score": 63,
"relevance_tier": "C"
}
Dashboard Charts
Recharts (React)
Basic Line Chart
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
function RevenueChart({ data }: { data: DataPoint[] }) {
return (
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="revenue" stroke="#8884d8" />
</LineChart>
</ResponsiveContainer>
);
}Multi-Series Bar Chart
import { BarChart, Bar, Legend } from 'recharts';
function ComparisonChart({ data }) {
return (
<ResponsiveContainer width="100%" height={300}>
<BarChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="thisYear" fill="#8884d8" name="2024" />
<Bar dataKey="lastYear" fill="#82ca9d" name="2023" />
</BarChart>
</ResponsiveContainer>
);
}D3.js Patterns
Reusable Chart Pattern
function createLineChart() {
let width = 600;
let height = 400;
let margin = { top: 20, right: 20, bottom: 30, left: 50 };
function chart(selection) {
selection.each(function(data) {
const svg = d3.select(this)
.selectAll('svg')
.data([data])
.join('svg')
.attr('width', width)
.attr('height', height);
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const x = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, innerWidth]);
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([innerHeight, 0]);
const line = d3.line()
.x(d => x(d.date))
.y(d => y(d.value));
const g = svg.selectAll('.chart-group')
.data([null])
.join('g')
.attr('class', 'chart-group')
.attr('transform', `translate(${margin.left},${margin.top})`);
g.selectAll('.line')
.data([data])
.join('path')
.attr('class', 'line')
.attr('d', line)
.attr('fill', 'none')
.attr('stroke', 'steelblue');
});
}
// Getter/setters for configuration
chart.width = function(_) {
return arguments.length ? (width = _, chart) : width;
};
return chart;
}Chart.js
import { Chart } from 'chart.js/auto';
const ctx = document.getElementById('myChart');
new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Red', 'Blue', 'Yellow'],
datasets: [{
data: [300, 50, 100],
backgroundColor: ['#ff6384', '#36a2eb', '#ffce56'],
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom' }
}
}
});Performance Tips
- Use
useMemofor data transformations - Implement windowing for large datasets
- Debounce resize handlers
- Use canvas-based charts for 10k+ points
Dashboard Filtering
Filter State Management
interface FilterState {
search: string;
dateRange: { start: Date; end: Date } | null;
categories: string[];
status: string[];
sortBy: string;
sortOrder: 'asc' | 'desc';
}
const defaultFilters: FilterState = {
search: '',
dateRange: null,
categories: [],
status: [],
sortBy: 'date',
sortOrder: 'desc',
};
// URL sync for shareable filters
function useFilterState() {
const [searchParams, setSearchParams] = useSearchParams();
const filters = useMemo(() => ({
search: searchParams.get('q') || '',
categories: searchParams.getAll('cat'),
status: searchParams.getAll('status'),
dateRange: parseDateRange(searchParams.get('dates')),
sortBy: searchParams.get('sort') || 'date',
sortOrder: (searchParams.get('order') || 'desc') as 'asc' | 'desc',
}), [searchParams]);
const setFilters = useCallback((updates: Partial<FilterState>) => {
setSearchParams(prev => {
const next = new URLSearchParams(prev);
if (updates.search !== undefined) {
updates.search ? next.set('q', updates.search) : next.delete('q');
}
if (updates.categories !== undefined) {
next.delete('cat');
updates.categories.forEach(c => next.append('cat', c));
}
// ... handle other filters
return next;
});
}, [setSearchParams]);
return [filters, setFilters] as const;
}Filter Components
Search Input
function SearchFilter({ value, onChange }: Props) {
const [local, setLocal] = useState(value);
const debouncedOnChange = useMemo(
() => debounce(onChange, 300),
[onChange]
);
return (
<input
type="search"
value={local}
onChange={(e) => {
setLocal(e.target.value);
debouncedOnChange(e.target.value);
}}
placeholder="Search..."
/>
);
}Multi-Select Facet
function FacetFilter({ options, selected, onChange, label }: Props) {
return (
<fieldset>
<legend>{label}</legend>
{options.map(option => (
<label key={option.value}>
<input
type="checkbox"
checked={selected.includes(option.value)}
onChange={(e) => {
const next = e.target.checked
? [...selected, option.value]
: selected.filter(v => v !== option.value);
onChange(next);
}}
/>
{option.label}
<span className="count">({option.count})</span>
</label>
))}
</fieldset>
);
}Date Range Picker
function DateRangeFilter({ value, onChange }: Props) {
const presets = [
{ label: 'Today', range: () => todayRange() },
{ label: 'Last 7 days', range: () => last7Days() },
{ label: 'Last 30 days', range: () => last30Days() },
{ label: 'This month', range: () => thisMonth() },
];
return (
<div className="date-range">
<div className="presets">
{presets.map(preset => (
<button
key={preset.label}
type="button"
onClick={() => onChange(preset.range())}
>
{preset.label}
</button>
))}
</div>
<div className="custom">
<input
type="date"
value={value?.start?.toISOString().split('T')[0] || ''}
onChange={(e) => onChange({
start: new Date(e.target.value),
end: value?.end || new Date(),
})}
/>
<span>to</span>
<input
type="date"
value={value?.end?.toISOString().split('T')[0] || ''}
onChange={(e) => onChange({
start: value?.start || new Date(),
end: new Date(e.target.value),
})}
/>
</div>
</div>
);
}Server-Side Filtering
// API query builder
function buildQuery(filters: FilterState): URLSearchParams {
const params = new URLSearchParams();
if (filters.search) {
params.set('q', filters.search);
}
if (filters.dateRange) {
params.set('start', filters.dateRange.start.toISOString());
params.set('end', filters.dateRange.end.toISOString());
}
filters.categories.forEach(c => params.append('category', c));
filters.status.forEach(s => params.append('status', s));
params.set('sort', `${filters.sortBy}:${filters.sortOrder}`);
return params;
}
// React Query integration
function useDashboardData(filters: FilterState) {
return useQuery({
queryKey: ['dashboard', filters],
queryFn: () => fetch(`/api/data?${buildQuery(filters)}`).then(r => r.json()),
});
}Active Filters Display
function ActiveFilters({ filters, onClear, onClearAll }: Props) {
const chips = useMemo(() => {
const result = [];
if (filters.search) {
result.push({ key: 'search', label: `"${filters.search}"` });
}
filters.categories.forEach(cat => {
result.push({ key: `cat:${cat}`, label: cat });
});
if (filters.dateRange) {
result.push({
key: 'dates',
label: `${formatDate(filters.dateRange.start)} - ${formatDate(filters.dateRange.end)}`,
});
}
return result;
}, [filters]);
if (chips.length === 0) return null;
return (
<div className="active-filters">
{chips.map(chip => (
<span key={chip.key} className="chip">
{chip.label}
<button onClick={() => onClear(chip.key)}>×</button>
</span>
))}
<button onClick={onClearAll}>Clear all</button>
</div>
);
}Real-Time Dashboards
WebSocket Connection
class DashboardWebSocket {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private listeners = new Map<string, Set<(data: any) => void>>();
connect(url: string) {
this.ws = new WebSocket(url);
this.ws.onopen = () => {
this.reconnectAttempts = 0;
this.subscribe(['metrics', 'alerts']);
};
this.ws.onmessage = (event) => {
const { type, data } = JSON.parse(event.data);
this.emit(type, data);
};
this.ws.onclose = () => {
this.scheduleReconnect();
};
}
subscribe(channels: string[]) {
this.ws?.send(JSON.stringify({ action: 'subscribe', channels }));
}
on(event: string, callback: (data: any) => void) {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event)!.add(callback);
}
private emit(event: string, data: any) {
this.listeners.get(event)?.forEach(cb => cb(data));
}
private scheduleReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) return;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
this.reconnectAttempts++;
setTimeout(() => this.connect(this.ws!.url), delay);
}
}React Hook
function useRealTimeData<T>(channel: string, initialData: T) {
const [data, setData] = useState<T>(initialData);
const wsRef = useRef<DashboardWebSocket>();
useEffect(() => {
const ws = new DashboardWebSocket();
ws.connect(import.meta.env.VITE_WS_URL);
ws.on(channel, (newData) => {
setData(prev => mergeData(prev, newData));
});
wsRef.current = ws;
return () => ws.disconnect();
}, [channel]);
return data;
}
// Usage
function MetricsDashboard() {
const metrics = useRealTimeData('metrics', { cpu: 0, memory: 0 });
return (
<div>
<Gauge value={metrics.cpu} label="CPU" />
<Gauge value={metrics.memory} label="Memory" />
</div>
);
}Server-Sent Events (SSE)
Simpler alternative for one-way updates:
function useSSE<T>(url: string) {
const [data, setData] = useState<T | null>(null);
useEffect(() => {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
setData(JSON.parse(event.data));
};
return () => eventSource.close();
}, [url]);
return data;
}Polling Fallback
function usePolling<T>(
fetcher: () => Promise<T>,
interval: number,
enabled = true
) {
const [data, setData] = useState<T | null>(null);
useEffect(() => {
if (!enabled) return;
const poll = async () => {
const result = await fetcher();
setData(result);
};
poll(); // Initial fetch
const id = setInterval(poll, interval);
return () => clearInterval(id);
}, [fetcher, interval, enabled]);
return data;
}Data Buffering
For high-frequency updates:
function useBufferedUpdates<T>(
source: Observable<T>,
flushInterval = 100
) {
const [data, setData] = useState<T[]>([]);
const bufferRef = useRef<T[]>([]);
useEffect(() => {
const sub = source.subscribe(item => {
bufferRef.current.push(item);
});
const flush = setInterval(() => {
if (bufferRef.current.length > 0) {
setData(prev => [...prev.slice(-100), ...bufferRef.current]);
bufferRef.current = [];
}
}, flushInterval);
return () => {
sub.unsubscribe();
clearInterval(flush);
};
}, [source, flushInterval]);
return data;
}Related skills
FAQ
What sub-skills does dashboard route to?
charts, real-time, and filtering.
What charting libraries does it mention?
D3, Recharts, or Chart.js.