Charts Plugin

A server-side chart data query builder and SVG chart component library for Svelar/SvelteKit. Query your database for chart-ready data with grouping, aggregation, and trend analysis, then render it with zero-dependency SVG chart components.

Package: @beeblock/svelar-charts

Install:

npx svelar plugin:install @beeblock/svelar-charts

Imports:

// Plugin registration
import { SvelarChartsPlugin } from '@beeblock/svelar-charts/server';

// Core API
import { ChartQuery, ChartDataBuilder } from '@beeblock/svelar-charts';

// Server-side (controller)
import { ChartController } from '@beeblock/svelar-charts/server';

// UI components
import { BarChart, LineChart, PieChart, DoughnutChart, AreaChart, SparkLine, StatCard, ChartCanvas, Tooltip, Legend } from '@beeblock/svelar-charts/ui';

// Types
import type { ChartData, ChartDataset, TrendChartData, TrendDirection, DateGrouping, BaseChartProps, BarChartProps, LineChartProps, PieChartProps, DoughnutChartProps, AreaChartProps, SparkLineProps, StatCardProps, SvelarChartsConfig } from '@beeblock/svelar-charts';

Quick Start

1. Register the Plugin

// src/lib/plugins.ts
import { SvelarChartsPlugin } from '@beeblock/svelar-charts/server';

export const chartsPlugin = new SvelarChartsPlugin({
  prefix: '/api',
  defaultColors: ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6'],
  animate: true,
});

2. Query Data and Render a Chart

// src/routes/dashboard/+page.server.ts
import { ChartQuery } from '@beeblock/svelar-charts';

export async function load() {
  const usersPerMonth = await ChartQuery.table('users')
    .count()
    .groupBy('month', 'created_at')
    .period('2026-01-01', '2026-12-31')
    .label('New Users')
    .get();

  return { usersPerMonth };
}
<!-- src/routes/dashboard/+page.svelte -->
<script lang="ts">
  import { BarChart } from '@beeblock/svelar-charts/ui';

  interface Props {
    data: { usersPerMonth: import('@beeblock/svelar-charts').ChartData };
  }
  let { data }: Props = $props();
</script>

<BarChart data={data.usersPerMonth} height={300} />

Configuration

The SvelarChartsPlugin constructor accepts:

Option Type Default Description
prefix string '/api' API route prefix
defaultColors string[] 10-color palette Default chart colors
animate boolean true Enable chart animations

Default color palette: #3B82F6 (blue), #10B981 (emerald), #F59E0B (amber), #EF4444 (red), #8B5CF6 (violet), #EC4899 (pink), #06B6D4 (cyan), #F97316 (orange), #14B8A6 (teal), #6366F1 (indigo).


Core API

ChartQuery

A fluent query builder that generates chart-ready data from database tables. Uses the Svelar Connection under the hood.

import { ChartQuery } from '@beeblock/svelar-charts';

Factory:

const query = ChartQuery.table('orders');

Fluent methods:

Method Returns Description
.table(name) ChartQuery Static factory -- start a new query on a table
.connection(name) this Use a named database connection
.select(expr) this Set a custom SQL select expression
.label(name) this Set the dataset label
.count() this Aggregate with COUNT(*)
.sum(column) this Aggregate with SUM(column)
.avg(column) this Aggregate with AVG(column)
.groupBy(column) this Group by a column (e.g. 'role', 'status')
.groupBy(interval, dateColumn) this Group by date interval ('day', 'week', 'month', 'quarter', 'year')
.where(column, value) this Add a WHERE clause (= operator)
.where(column, operator, value) this Add a WHERE clause with custom operator
.period(start, end) this Filter to a date range (inclusive)
.compareWith(previousPeriod?) this Compare with the previous period (for trends)
.get() Promise<ChartData> Execute and return { labels, datasets }
.trend(interval, days) Promise<TrendChartData> Execute with trend comparison

Examples:

// Count users by role
const usersByRole = await ChartQuery.table('users')
  .count()
  .groupBy('role')
  .label('Users')
  .get();
// => { labels: ['admin', 'editor', 'user'], datasets: [{ label: 'Users', data: [5, 12, 89] }] }

// Monthly revenue over a year
const revenue = await ChartQuery.table('orders')
  .sum('total')
  .groupBy('month', 'created_at')
  .period('2026-01-01', '2026-12-31')
  .where('status', 'completed')
  .label('Revenue')
  .get();

// Trend: new users in the last 30 days vs. the previous 30 days
const trend = await ChartQuery.table('users')
  .count()
  .label('New Users')
  .trend('day', 30);
// => { labels, datasets, change: 15.2, direction: 'up', currentValue: 142, previousValue: 123 }

Date grouping intervals (DateGrouping):

Interval SQL Expression Label Format
'day' strftime('%Y-%m-%d', ...) 'Jan 15'
'week' strftime('%Y-W%W', ...) 'W05'
'month' strftime('%Y-%m', ...) 'Jan'
'quarter' derived 'Q1'
'year' strftime('%Y', ...) '2026'

ChartData

The standard data structure consumed by all chart components:

interface ChartData {
  labels: string[];
  datasets: ChartDataset[];
}

interface ChartDataset {
  label?: string;
  data: number[];
  color?: string;
  backgroundColor?: string;
  borderColor?: string;
  borderWidth?: number;
  hidden?: boolean;
}

TrendChartData

Extended ChartData with period comparison info:

interface TrendChartData extends ChartData {
  change: number;          // percentage change (e.g. 15.2 or -8.3)
  direction: 'up' | 'down' | 'flat';
  currentValue: number;    // total for the current period
  previousValue: number;   // total for the previous period
}

ChartDataBuilder

Manually construct ChartData when you already have the data:

import { ChartDataBuilder } from '@beeblock/svelar-charts';

// Fluent builder
const data = new ChartDataBuilder()
  .labels(['Jan', 'Feb', 'Mar', 'Apr'])
  .dataset('Revenue', [1200, 1800, 2400, 2100], { color: '#3B82F6' })
  .dataset('Expenses', [800, 900, 1100, 950], { color: '#EF4444' })
  .build();

// From a key-value map
const pie = ChartDataBuilder.fromMap(
  { Chrome: 65, Firefox: 15, Safari: 12, Other: 8 },
  'Browser Share',
);

// From an array of objects
const bar = ChartDataBuilder.fromArray(
  products,
  'name',    // label key
  'sales',   // value key
  'Product Sales',
);

Server-Side

ChartController

Provides static methods for serving chart data as JSON:

// src/routes/api/charts/users/+server.ts
import { ChartController } from '@beeblock/svelar-charts/server';
import { ChartQuery } from '@beeblock/svelar-charts';

// Serve a pre-built query
export const GET = async (event) => {
  const query = ChartQuery.table('users')
    .count()
    .groupBy('month', 'created_at')
    .period('2026-01-01', '2026-12-31')
    .label('Users');

  return ChartController.serve(query);
};
// Serve trend data
export const GET = async (event) => {
  const query = ChartQuery.table('orders')
    .sum('total')
    .label('Revenue');

  return ChartController.serveTrend(query, 'month', 90);
};
// Generic handler with dynamic query params
export const GET = async (event) => {
  return ChartController.handle(event, (query, params) => {
    const groupBy = params.get('group_by') || 'month';
    return query.count().groupBy(groupBy, 'created_at');
  });
};
Method Description
ChartController.serve(query) Execute a ChartQuery and return JSON response
ChartController.serveTrend(query, interval, days) Execute a trend query and return JSON response
ChartController.handle(event, builder) Generic handler that builds a query from URL params

UI Components

All chart components render pure SVG with no external dependencies.

BarChart

<script lang="ts">
  import { BarChart } from '@beeblock/svelar-charts/ui';
</script>

<BarChart
  data={chartData}
  height={300}
  width={600}
  horizontal={false}
  stacked={false}
  barRadius={4}
  barGap={4}
  showValues={false}
  showLegend={true}
  animate={true}
/>
Prop Type Default Description
data ChartData required Chart data
height number 300 Chart height in pixels
width number 600 Chart width in pixels
colors string[] default palette Bar colors
className string '' CSS class
animate boolean true Enable animations
horizontal boolean false Horizontal bar layout
stacked boolean false Stack bars
barRadius number 4 Bar corner radius
barGap number 4 Gap between bars
showValues boolean false Display values on bars
showLegend boolean true Show legend

LineChart

<script lang="ts">
  import { LineChart } from '@beeblock/svelar-charts/ui';
</script>

<LineChart
  data={chartData}
  height={300}
  smooth={false}
  showArea={false}
  showDots={true}
  dotRadius={4}
  strokeWidth={2}
  showLegend={true}
/>
Prop Type Default Description
data ChartData required Chart data
height number 300 Height in pixels
width number 600 Width in pixels
colors string[] default palette Line colors
className string '' CSS class
animate boolean true Enable animations
smooth boolean false Smooth (bezier) lines
showArea boolean false Fill area under lines
showDots boolean true Show data point dots
dotRadius number 4 Dot radius
strokeWidth number 2 Line stroke width
showLegend boolean true Show legend

PieChart

<PieChart
  data={chartData}
  size={300}
  showLabels={true}
  showPercentages={true}
  donut={false}
  donutWidth={60}
/>
Prop Type Default Description
data ChartData required Chart data
size number undefined Chart diameter
colors string[] default palette Slice colors
donut boolean false Render as donut
donutWidth number undefined Donut ring width
showLabels boolean false Show labels
showPercentages boolean false Show percentage values
className string '' CSS class
animate boolean true Enable animations

DoughnutChart

<DoughnutChart
  data={chartData}
  size={300}
  ringWidth={60}
  showLabels={true}
  showPercentages={true}
  centerLabel="Total"
  centerValue="1,234"
/>
Prop Type Default Description
data ChartData required Chart data
size number undefined Chart diameter
colors string[] default palette Ring colors
ringWidth number undefined Ring width
showLabels boolean false Show labels
showPercentages boolean false Show percentage values
centerLabel string undefined Center text label
centerValue string undefined Center text value
className string '' CSS class
animate boolean true Enable animations

AreaChart

<AreaChart
  data={chartData}
  height={300}
  gradient={true}
  smooth={true}
  fillOpacity={0.3}
  showDots={true}
/>
Prop Type Default Description
data ChartData required Chart data
height number undefined Height in pixels
width number undefined Width in pixels
colors string[] default palette Area colors
className string '' CSS class
animate boolean true Enable animations
gradient boolean false Use gradient fill
smooth boolean false Smooth lines
showDots boolean false Show data point dots
dotRadius number undefined Dot radius
strokeWidth number undefined Line stroke width
fillOpacity number undefined Area fill opacity (0-1)

SparkLine

A minimal inline chart for dashboards and tables:

<SparkLine
  data={[10, 15, 8, 22, 18, 25, 30]}
  width={120}
  height={32}
  color="#3B82F6"
  strokeWidth={1.5}
  showEndDot={true}
/>
Prop Type Default Description
data number[] required Array of numeric values
width number undefined Width in pixels
height number undefined Height in pixels
color string undefined Line color
strokeWidth number undefined Line stroke width
showEndDot boolean false Show a dot at the last data point
className string '' CSS class

StatCard

A stat card with title, value, trend indicator, and optional sparkline:

<StatCard
  title="Total Revenue"
  value="$12,345"
  change={15.2}
  trend="up"
  sparkData={[100, 120, 115, 140, 155, 170]}
  sparkColor="#10B981"
/>
Prop Type Default Description
title string required Card title
value string required Formatted value to display
change number undefined Percentage change
trend 'up' | 'down' | 'flat' undefined Trend direction
sparkData number[] undefined Data for inline sparkline
sparkColor string '#3B82F6' Sparkline color
icon any undefined Icon component (Lucide or Tabler)
className string '' CSS class

Full Working Example

// src/routes/dashboard/+page.server.ts
import { ChartQuery } from '@beeblock/svelar-charts';

export async function load() {
  const usersTrend = await ChartQuery.table('users')
    .count()
    .label('New Users')
    .trend('month', 365);

  const revenueByMonth = await ChartQuery.table('orders')
    .sum('total')
    .groupBy('month', 'created_at')
    .period('2026-01-01', '2026-12-31')
    .where('status', 'completed')
    .label('Revenue')
    .get();

  const usersByRole = await ChartQuery.table('users')
    .count()
    .groupBy('role')
    .label('Users by Role')
    .get();

  return { usersTrend, revenueByMonth, usersByRole };
}
<!-- src/routes/dashboard/+page.svelte -->
<script lang="ts">
  import { StatCard, BarChart, PieChart } from '@beeblock/svelar-charts/ui';

  interface Props {
    data: {
      usersTrend: import('@beeblock/svelar-charts').TrendChartData;
      revenueByMonth: import('@beeblock/svelar-charts').ChartData;
      usersByRole: import('@beeblock/svelar-charts').ChartData;
    };
  }
  let { data }: Props = $props();
</script>

<div class="dashboard-grid">
  <StatCard
    title="New Users"
    value={String(data.usersTrend.currentValue)}
    change={data.usersTrend.change}
    trend={data.usersTrend.direction}
    sparkData={data.usersTrend.datasets[0]?.data}
    sparkColor="#3B82F6"
  />

  <BarChart data={data.revenueByMonth} height={350} showValues={true} />

  <PieChart data={data.usersByRole} size={300} showLabels={true} showPercentages={true} />
</div>
// src/routes/api/charts/revenue/+server.ts
import { ChartController } from '@beeblock/svelar-charts/server';
import { ChartQuery } from '@beeblock/svelar-charts';

export const GET = async (event) => {
  const query = ChartQuery.table('orders')
    .sum('total')
    .groupBy('month', 'created_at')
    .period('2026-01-01', '2026-12-31')
    .where('status', 'completed')
    .label('Revenue');

  return ChartController.serve(query);
};
Svelar © 2026 · MIT License