Go Dashboard is a modular toolkit for building server rendered admin dashboards in Go. It provides a layout service, HTTP controller, template renderer, router adapters, CLI tooling, and a provider system so you can compose dashboards from reusable widget definitions stored anywhere (database, CMS, or custom service).
- Layout orchestration with per-viewer preferences, telemetry hooks, and WebSocket refresh/broadcast helpers.
- Router-agnostic HTTP/JSON/WebSocket endpoints (first-class support for go-router) plus a thin façade in
pkg/dashboard. - Widget provider registry with analytics/chart helpers, schema validation, and manifest-driven discovery/CLI scaffolding.
- Localization helpers for templates and providers, plus translation-aware analytics/chart payloads.
- Optional application shell primitives for workbench-style admin modules with side rails, splitters, focus mode, browser-state restore, and theme-aware chrome.
components/dashboard– dashboard service, HTTP controllers, commands, queries, templates, and transport adapters.components/dashboard/httpapi– router-agnostic executor interface backed by shared commands.components/dashboard/gorouter– helpers that register dashboard routes (HTML/JSON/REST/WebSocket) on anygo-routeradapter.pkg/dashboard– thin façade exposing the service to consumers.pkg/analytics– helper clients/repositories for wiring real BI/observability data into analytics widgets.pkg/goadmin– helper utilities for wiring dashboards into go-admin (feature flags, menu seeding).
- Add go-dashboard to your module and implement
WidgetStore(wrap go-cms, a custom DB, etc.). - Build a
dashboard.Servicewith any optional dependencies you need (Authorizer,PreferenceStore, telemetry hooks, analytics providers, etc.). - Seed the dashboard (areas, definitions, default layout) once at bootstrap time.
- Mount the go-router adapter (or wire your own
httpapi.Executor) so the HTML, JSON, REST, and WebSocket routes are available.
import (
router "github.com/goliatone/go-router"
"github.com/goliatone/go-dashboard/components/dashboard"
"github.com/goliatone/go-dashboard/components/dashboard/commands"
"github.com/goliatone/go-dashboard/components/dashboard/gorouter"
"github.com/goliatone/go-dashboard/components/dashboard/httpapi"
)
service := dashboard.NewService(dashboard.Options{WidgetStore: myWidgetStore})
renderer, _ := dashboard.NewTemplateRenderer()
controller := dashboard.NewController(dashboard.ControllerOptions{
Service: service,
Renderer: renderer,
})
executor := &httpapi.CommandExecutor{
AssignCommander: commands.NewAssignWidgetCommand(service, nil),
RemoveCommander: commands.NewRemoveWidgetCommand(service, nil),
ReorderCommander: commands.NewReorderWidgetsCommand(service, nil),
RefreshCommander: commands.NewRefreshWidgetCommand(service, nil),
}
hook := dashboard.NewBroadcastHook()
app := router.New()
_ = gorouter.Register(gorouter.Config[struct{}]{
Router: app,
Controller: controller,
API: executor,
Broadcast: hook,
})See docs/TRANSPORTS.md for details on the go-router adapter, executor interface,
and broadcast hook patterns. For a full working sample (including widget seeding,
custom providers, and menu bootstrapping) check examples/goadmin/main.go.
Each route defaults to /admin/..., but you can override individual paths (HTML,
layout JSON, CRUD endpoints, preferences, WebSocket) via gorouter.RouteConfig
while keeping the same controller/executor wiring.
analytics_funnel, cohort_overview, and alert_trends templates ship with DI-friendly providers so dashboards can surface BI/observability data without embedding transport logic. Configuration payloads are validated via JSON schema before widgets hit the store, and templates expose CSS hooks for fine-grained styling. Use pkg/analytics to wrap your HTTP BI or observability clients and pass the resulting repositories into dashboard.New*AnalyticsProvider. See docs/ANALYTICS.md for schemas, provider interfaces, and screenshots.
Admin Request (go-router)
|
v
Controller (HTML/JSON)
|
v
dashboard.Service -----> PreferenceStore
| Authorizer
| Telemetry
v
WidgetStore (go-cms) <---- ProviderRegistry ----> Custom Providers
|
v
RefreshHook / BroadcastHook ---> WebSocket / Notifications transports
Per-user layout overrides are persisted through the new preferences endpoint:
POST /admin/dashboard/preferences
{
"area_order": {
"admin.dashboard.main": ["widget-2", "widget-1"]
},
"layout_rows": {
"admin.dashboard.main": [
{
"widgets": [
{"id": "widget-2", "width": 6},
{"id": "widget-1", "width": 6}
]
}
]
},
"hidden_widget_ids": ["widget-3"]
}
The route uses the authenticated viewer from go-router, so transports only need to send the desired ordering/hidden widgets (plus optional layout_rows to describe per-row widths). The data flows through dashboard.SavePreferences → PreferenceStore, and overrides are applied automatically during ConfigureLayout, which annotates each widget’s metadata with layout.width, layout.row, etc.
Modules that need a workbench layout can opt into dashboard.Shell without
changing existing widget dashboards. Add shell assets with
PageAssets.AddShellAssets(""), serve them from /dashboard/assets/shell/, and
populate Page.Shell with module-owned region content. The shell runtime is
framework-free and supports collapse, resize, keyboard resize, focus mode, and
versioned browser storage keyed by surface and viewer fallback.
See components/dashboard/README.md for the data-attribute contract, theme
tokens, validation command, and go-admin migration handoff criteria.
Manifest-driven discovery lets third-party widgets register without touching Go
code. Author manifests (see docs/DISCOVERY.md and the samples under
docs/manifests/), then load them via
registry.LoadManifestFile("docs/manifests/community.widgets.yaml"). The
cmd/widgetctl binary (also exposed as ./taskfile dashboard:widgets:scaffold)
generates manifest entries, JSON schemas, and provider stubs in one step:
./taskfile dashboard:widgets:scaffold \
--code community.widget.pipeline_health \
--name "Pipeline Health" \
--description "Tracks CI/CD durations and failure counts." \
--manifest docs/manifests/community.widgets.yamlCI guardrails in Task 9.4 validate manifests for duplicates and schema issues so
broken widget packs never land in main.
go-dashboard now mirrors go-cms localization flows:
- Router adapters (including go-router) propagate the locale discovered from URL
prefixes, query params, or
Accept-LanguageintoViewerContext.Locale. dashboard.Optionsaccepts an optionalTranslationService; the default renderer wires it viadashboard.WithTranslationHelpers, exposing aTfunction to templates ({{ T("dashboard.widget.system_status.title", locale, "System Status") }}).- Providers receive the same translator through
WidgetContext.Translatorso server-side strings (quick action labels, system status names, etc.) can be localized alongside data. - Widget definitions/manifests accept
name_localizedanddescription_localizedmaps. Usedashboard.ResolveLocalizedValueto pick the best translation with graceful fallback to the default string. - The sample app (
examples/goadmin) demonstrates locale switching via?locale=es, including localized quick actions, activity feed verbs, and welcome messages without changing transport code.
./taskfile dashboard:test– run the focused dashboard test suite../taskfile dashboard:lint– run golangci-lint across dashboard packages../taskfile go:lint:new– check new lint findings againstorigin/main../taskfile go:quality:pr– run the local PR gate: format check, tests, and change-scoped lint../taskfile go:quality:all– run the full local quality path, including race tests and security scans.
See docs/TROUBLESHOOTING.md if bootstrap, authorization, or provider issues appear during integration.
Bar/line/pie widgets are rendered server-side through go-echarts. Check
docs/ECHARTS_WIDGETS.md for configuration payloads, CSP guidance, and
troubleshooting.
Dashboard chrome, widget states, and charts can consume the optional semantic
theme contract documented in components/dashboard/README.md. go-dashboard
keeps its structural ThemeProvider interface and has no required go-theme
dependency; named/custom ECharts themes remain compatible with semantic
series and tooltip overrides, while Cartesian providers additionally consume
axis and grid tokens. Dashboard CSS and diagnostics are derived from the
concrete page inventory so partial themes do not reset unrelated host styles.
- Sample app:
go run ./examples/goadmincreates chart widgets in the demo dashboard. - Dynamic sales widgets are powered by
SalesChartProvider, which can query any repository that satisfiesSalesSeriesRepositoryand optionally cache render output viadashboard.NewChartCache. - Inspect the JSON layout via:
curl -s http://localhost:9876/admin/dashboard/_layout \ | jq '.areas[].widgets[] | select(.definition|endswith("_chart"))'
- Design + roadmap live in
CHARTS_FEATURE.md/CHARTS_TSK.md.
./taskfile dashboard:serve:chartsenables Fiber’s compression middleware (GO_DASHBOARD_ENABLE_GZIP=1) and serves the bundled ECharts assets; setGO_DASHBOARD_ECHARTS_CDNto point at a CDN/self-hosted bucket instead../taskfile dashboard:assets:echartsrefreshes the embedded ECharts runtime + themes from the upstream go-echarts asset bundle../taskfile dashboard:bench:chartsruns the newBenchmarkECharts*targets to compare cached vs. uncached rendering cost.- Set
dashboard.Options.ScriptNoncewhen building the service to stamp CSP-approved nonces onto every inline<script>emitted by go-echarts.