README file from
GithubBases Chart
Visualize your Obsidian Bases data as charts — directly inside your notes.
Features
- 8 chart types — bar, column (horizontal bar), pie, doughnut, gauge (half-doughnut), line, GitHub-style calendar heatmap, and stat (single-number KPI)
- SQL query language — familiar syntax for querying your Bases data
- Visual configuration — inline settings panel with dropdowns, toggles, draggable color palette, and live SQL preview
- Multi-metric queries — count multiple conditions in a single chart (e.g. "missing tags" vs "missing dates")
- Multi-source — compare data across multiple
.basefiles side-by-side, or merge them with UNION - Date bucketing —
GROUP BY month(file.ctime)/year(...)/day(...)for time-series charts - Relative-time functions —
today(),now(),daysSince(),daysUntil(),year(),month(),day() - Sorting — order bars by value or label, ascending or descending
- Data labels — show values at base, top, or outside of bars/slices with callout support for pie charts
- Appearance controls — custom colors with drag-to-reorder, gridline toggle, legend toggle, font size and colour for stat charts, configurable chart height
Quick Start
Add a fenced code block with bases-chart and write a SQL query:
```bases-chart
type: bar
sql: SELECT COUNT(*) FROM "Todos.base" GROUP BY status
```
Or start with an empty block and use the gear icon to configure visually:
```bases-chart
```
You can also insert a chart via the command palette: Insert bases chart.
Configuration
The YAML config has two parts: appearance (stored in YAML) and data (stored as SQL):
type: bar # chart type (bar | column | pie | doughnut | gauge | line | calendar | stat)
sql: SELECT COUNT(*) FROM "Todos.base" GROUP BY status
title: My Chart # optional title
showGridlines: true # show/hide gridlines
showLegend: true # show/hide legend
dataLabels: outside # none | base | top | outside
colors: # custom color palette
- "#4e79a7"
- "#f28e2b"
height: 400 # chart height in pixels (default 400; 200 for stat)
fontSize: 72 # stat chart only — number font size in pixels (default 72)
fontColor: "#4e79a7" # stat chart only — hex colour; omit to inherit the theme
SQL Reference
Basic queries
-- Count notes grouped by a property
SELECT COUNT(*) FROM "Todos.base" GROUP BY status
-- Show a value per note
SELECT file.name, Priority FROM "Projects.base/Starred"
-- Computed values
SELECT daysSince(file.ctime) FROM "Todos.base/Starred" ORDER BY value DESC
-- Aggregate with grouping
SELECT AVG(rating) FROM "Books.base" GROUP BY categories
SELECT clause
| Syntax | Meaning |
|---|---|
SELECT file.name, Rank |
Per-note: label + value |
SELECT daysSince(file.ctime) |
Per-note: computed value |
SELECT COUNT(*) |
Count (use with GROUP BY) |
SELECT SUM(prop) |
Sum values per group |
SELECT AVG(prop) |
Average values per group |
FROM clause
| Syntax | Meaning |
|---|---|
FROM "Todos.base" |
Use first view from a Base |
FROM "Todos.base/Starred" |
Use a specific named view |
FROM "A.base", "B.base" |
Separate datasets (grouped bars) |
FROM "A.base" UNION "B.base" |
Merge into one pool |
FROM notes |
All notes in vault |
WHERE clause
Top-level WHERE supports tag and folder filters:
WHERE tags CONTAINS 'project'
WHERE folder = 'Work'
WHERE tags CONTAINS 'todo' AND folder = 'Work'
Per-metric WHERE (inside a multi-metric SELECT) supports a richer
comparator set, including relative-time functions and date-bucket
wrappers on the left-hand side:
-- Operators: =, !=, >, >=, <, <=, CONTAINS, IS EMPTY, IS NOT EMPTY
COUNT(*) WHERE status != 'done'
COUNT(*) WHERE Priority >= 3
COUNT(*) WHERE date IS NOT EMPTY
-- today() expands to 'YYYY-MM-DD' (local date)
COUNT(*) WHERE day(file.ctime) = today() -- created today
COUNT(*) WHERE month(file.ctime) = month(today()) -- created this month (see note below)
-- now() expands to current epoch milliseconds
COUNT(*) WHERE file.mtime >= 1760000000000 -- modified since this timestamp
Note:
month(today())currently requirestoday()to resolve to a value whosemonth(...)wrapper produces the same bucket format. The cleanest pattern for "created this month" is still to group by month and pick the current bucket — or use a literal'2026-04'.
GROUP BY
GROUP BY status -- by a property
GROUP BY day(file.ctime) -- per-day bucket → "YYYY-MM-DD"
GROUP BY month(file.ctime) -- per-month bucket → "YYYY-MM"
GROUP BY year(file.ctime) -- per-year bucket → "YYYY"
Date-bucket grouping works on any date property — frontmatter dates
(GROUP BY month(published)) as well as file timestamps.
Multi-metric SELECT
Each metric gets its own bar/slice:
SELECT
COUNT(*) WHERE tags IS EMPTY AS "Missing Tags",
COUNT(*) WHERE topics IS EMPTY AS "Missing Topics",
COUNT(*) WHERE date IS EMPTY AS "Missing Dates"
FROM "Todos.base"
ORDER BY
ORDER BY value DESC -- sort by bar height
ORDER BY value ASC -- smallest first
ORDER BY label ASC -- alphabetical
ORDER BY label DESC -- reverse alphabetical
Built-in functions
| Function | Where it's valid | Description |
|---|---|---|
daysSince(prop) |
SELECT |
Days between property date and today (per note) |
daysUntil(prop) |
SELECT |
Days from today to property date (per note) |
year(prop) |
SELECT, GROUP BY, WHERE LHS |
Year bucket → "YYYY" |
month(prop) |
SELECT, GROUP BY, WHERE LHS |
Month bucket → "YYYY-MM" |
day(prop) |
SELECT, GROUP BY, WHERE LHS |
Day bucket → "YYYY-MM-DD" |
today() |
WHERE RHS (per-metric) |
Expands to 'YYYY-MM-DD' (local date) |
now() |
WHERE RHS (per-metric) |
Expands to current epoch milliseconds |
Chart Types
| Type | Description |
|---|---|
bar |
Vertical bar chart |
column |
Horizontal bar chart |
pie |
Pie chart |
doughnut |
Doughnut chart (pie with center hole) |
gauge |
Half-doughnut (top hemisphere only) |
line |
Line chart |
calendar |
GitHub-style contribution heatmap (52 weeks) |
stat |
Single large number — ideal for dashboards / KPI tiles |
Examples
Todo age ranking
type: bar
sql: SELECT daysSince(file.ctime) FROM "Todos.base/Starred" ORDER BY value DESC
dataLabels: outside
showGridlines: false
Project status gauge
type: gauge
sql: SELECT COUNT(*) FROM "Projects.base" GROUP BY status
dataLabels: outside
Note activity calendar
type: calendar
sql: SELECT COUNT(*) FROM "Todos.base"
title: Note Activity
Data quality audit
type: bar
sql: >
SELECT
COUNT(*) WHERE tags IS EMPTY AS "No Tags",
COUNT(*) WHERE topics IS EMPTY AS "No Topics",
COUNT(*) WHERE date IS EMPTY AS "No Date"
FROM "Todos.base"
dataLabels: outside
Notes created today (stat tile)
type: stat
title: Notes created today
sql: SELECT COUNT(*) WHERE day(file.ctime) = today() AS "Today" FROM notes
fontSize: 96
fontColor: "#4e79a7"
Notes created each month (time series)
type: bar
title: Notes per month
sql: SELECT COUNT(*) FROM notes GROUP BY month(file.ctime) ORDER BY label asc
dataLabels: top
showLegend: false
KPI row — three stat tiles side-by-side
Drop these in a 3-column layout (e.g. the Dashboards plugin) for a tidy summary strip:
type: stat
title: Total notes
sql: SELECT COUNT(*) AS "Notes" FROM notes
type: stat
title: Created this month
sql: SELECT COUNT(*) WHERE month(file.ctime) = '2026-04' AS "This month" FROM notes
fontColor: "#59a14f"
type: stat
title: Modified in last 24h
sql: SELECT COUNT(*) WHERE file.mtime >= 1760000000000 AS "Last 24h" FROM notes
fontColor: "#f28e2b"
Installation
From Obsidian
- Open Settings → Community Plugins → Browse
- Search for Bases Chart
- Click Install, then Enable
From source
cd /path/to/vault/.obsidian/plugins
git clone https://github.com/kevinmcaleer/obsidian-bases-chart.git
cd bases-chart
npm install
npm run build
Restart Obsidian and enable the plugin in Settings → Community Plugins.
Manual
- Download
main.js,manifest.json, andstyles.cssfrom the latest release - Create a folder
bases-chartinside.obsidian/plugins/ - Copy the three files into that folder
- Restart Obsidian and enable the plugin
Development
npm install
npm run dev # one-shot build with source maps
npm run build # production build (minified, auto-bumps version)
License
MIT