Download the PHP package mddev31/filament-dynamic-dashboard without Composer

On this page you can find all versions of the php package mddev31/filament-dynamic-dashboard. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package filament-dynamic-dashboard

Filament Dynamic Dashboard

End-user-configurable dashboards for Filament v4/5 — drag, resize, and move widgets across named sections, with layouts defined as plain JSON files.

PHP 8.3+ Filament 4/5 Laravel 10/11/12

Introduction

Filament Dynamic Dashboard turns Filament's static widget grid into a fluid, end-user-configurable dashboard system. Drag widgets anywhere on the canvas, resize them from any corner, move them between named sections, and the layout persists in a single round-trip.

Widgets carry size constraints declared on the class itself, so a chart can lock its height while still letting users resize width; container resizes propagate to chart libraries automatically.

Per-dashboard filters, default filter values, per-filter visibility, and optional Spatie role-based access all work out of the box.

Built for Filament v4+, Laravel 10+, and powered by GridStack.js under the hood.

Key capabilities

Requirements

Installation

Install via Composer:

Publish and run the migrations:

Publish the JavaScript and CSS (GridStack + this package's own bundle):

Filament's panel layout injects them automatically — no <link> or <script> tags to add yourself.

Optionally publish the configuration file:

Optionally publish translations:

Upgrading from version under 1.x

From v1.x replaces the database-backed grids/blocks with JSON layout templates and adds drag-and-resize via GridStack. Follow these steps in order:

  1. Pull the new migration stub.

    Your existing create_dynamic_dashboard_tables migration is left untouched. A new upgrade_dynamic_dashboard_tables_to_v2 migration is added next to it.

  2. Back up your database. The upgrade is intentionally not reversible — the down() step throws on purpose. Take a snapshot before continuing.

  3. Run the migration.

    The upgrade migration:

    • Adds template_key on dashboards and section_slug, x, y, w, h on dashboard_widgets.
    • Copies existing widget data: every widget gets section_slug = 'main', w = old columns, y = old ordering, h = 1, x = 0. GridStack compacts the layout on first render.
    • Every dashboard gets template_key = 'flat-12' (the default single-section layout). Pick a different one in the manager afterwards if you want.
    • Drops the obsolete dashboard_grid_id foreign key on dashboards, the columns, ordering, dashboard_grid_block_id columns on dashboard_widgets, and the dashboard_grids and dashboard_grid_blocks tables.
    • Adds dashboards.is_personal (boolean, default false) and dashboards.created_by (nullable foreign key to your users table, resolved from config('auth.providers.users.model')). Existing rows stay global (is_personal = false, created_by = null) until you flip them in the manager. See Personal dashboards.
  4. Publish the new assets.

  5. Update your widget classes. v2 adds 6 static size methods to the DynamicWidget contract (getDynamicDashboardDefaultWidth, …Min/MaxHeight). The fastest fix is to add use HasSizeDefaults; to every widget class — sensible defaults are provided and you only override the axes you constrain. See Helper traits.

  6. Clear caches.

What survives the upgrade: dashboard names, descriptions, page assignments, filters, default filter values, widget names, types, settings, display_title, active/locked flags. What's intentionally lost: nested block hierarchies, per-instance widget sizes (now declared on the class), per-widget ordering selectors (drag instead).

Quick Start

A minimal dashboard page and a minimal widget:

Register the widget on your Filament panel as you would any other widget, visit the dashboard page, click Widget, pick Simple Stats, and drag it around. Done.

Creating a Dashboard Page

Extend DynamicDashboard. All standard Filament Page features (navigation icon, slug, group, etc.) remain available. Layout (parent column count, sections) is driven by the dashboard's template_key — see Layout Templates.

Overridable methods

Method Signature Purpose
getDashboardFilters() static array Return Filament Field components shown in the filter bar.
getDefaultFilterSchema() static array Return custom fields for editing default filter values (keyed by filter name).
resolveFilterDefaults() static array Transform stored defaults into actual filter values at apply time.
canEdit() static bool Whether the current user can add/edit/delete widgets and manage dashboards.
canDisplay() static bool Whether the current user can view a given dashboard.
showWidgetLoader() static bool Whether widgets show a loading overlay during their own Livewire commits.

Creating a Dynamic Widget

Any Filament widget can become a dynamic widget by implementing the DynamicWidget interface. The contract has three identity methods and six size methods:

Method Returns Purpose
getWidgetLabel() string Display name shown in the widget type selector.
getSettingsFormSchema() array<Component> Filament form components for widget-specific settings.
getSettingsCasts() array<string, string> Cast definitions for settings values (primitives, BackedEnums, arrays).
getDynamicDashboardDefaultWidth() int Width (columns) of new instances of this widget.
getDynamicDashboardDefaultHeight() int Height (rows) of new instances of this widget.
getDynamicDashboardMinWidth() / …MaxWidth() int Width resize range. Set both equal to lock width.
getDynamicDashboardMinHeight() / …MaxHeight() int Height resize range. Set both equal to lock height.

Why the prefix? Filament chart widgets define an instance method getMaxHeight(): ?string. PHP forbids a child class from overriding an inherited instance method with a static one of the same name, so the getDynamicDashboard… prefix avoids the collision.

Helper traits

Trait Provides
MDDev\DynamicDashboard\Concerns\HasSizeDefaults Sensible defaults for all six size methods (default 4×1, min 1×1, max 12×12). Override only what you constrain.
MDDev\DynamicDashboard\Concerns\HasEmptySettings Empty getSettingsFormSchema() and getSettingsCasts() for widgets without configurable settings.

Simple widget (no settings, default size)

Custom size constraints

Override only the axes you want to constrain — the trait keeps the rest as defaults:

Widget with settings

How settings work

Three pieces are linked by a shared key name:

Piece Role
public ResultTypeEnum $resultType Livewire property that receives the value at render time.
Select::make('resultType') in getSettingsFormSchema() Form field the admin fills in (stored as JSON in the DB).
'resultType' => ResultTypeEnum::class in getSettingsCasts() Type-cast rule applied when reading the JSON back.

The key name must be identical across all three. The form field name becomes the JSON key, which is then cast and injected into the matching public property.

Hydration flow:

Give every public property a default value — if a setting hasn't been saved yet, that default is used.

Settings casts

Cast Example Description
'int', 'integer' 'limit' => 'int' Cast to integer.
'float', 'double' 'ratio' => 'float' Cast to float.
'string' 'label' => 'string' Cast to string.
'bool', 'boolean' 'enabled' => 'bool' Cast to boolean.
MyEnum::class 'type' => ResultTypeEnum::class Cast to a BackedEnum via tryFrom().
['array', MyEnum::class] 'types' => ['array', ResultTypeEnum::class] Cast each element of an array to an enum.

Restricting a widget to specific pages

Implement the optional availableForDashboard() method on your widget:

An empty array (or omitting the method entirely) makes the widget available on every dynamic dashboard.

Widget visibility

Filament's canView() is respected automatically. If it returns false, the widget is hidden from the type selector and not rendered.

Widget metadata

Each widget can declare two optional public properties to receive its own id and title from the dashboard:

Declare only the ones you need.

Loading indicator

Every widget shows a loading overlay while its own Livewire component is committing. Toggle globally with showWidgetLoader() on the dashboard page, or per widget with an optional static showLoader() method:

Resolution order: per-widget showLoader() if it returns a non-null boolean; otherwise the dashboard's showWidgetLoader() (default true).

Resize-aware widgets

When a user drags a widget's resize handle, GridStack updates the container size — but chart libraries render their canvas at mount-time dimensions and don't know the box has changed. On every resizestop and dragstop, this package broadcasts two signals:

  1. A native window.resize event — ApexCharts, Chart.js, ECharts, Plotly all listen for this and re-fit themselves automatically.
  2. A Livewire event dynamic-dashboard:widget-resized with the resized widget's id — for widgets that need explicit control.

For the auto-resize to actually use the new size, the widget's own chart options must use responsive sizing:

ApexCharts — set chart.height: '100%' in getOptions():

Chart.js (Filament native ChartWidget) — disable aspect-ratio locking:

Custom rendering / lazy loads — listen to the Livewire event:

Layout Templates (JSON)

Templates are plain JSON files on disk. Each declares a parent column count and an ordered list of sections — named zones that host widgets. The package ships 10 presets and a dashboard references one via its template_key.

Template file format

A template is a JSON file in any directory listed by config('filament-dynamic-dashboard.template_paths'). The package's own preset directory is always loaded first; later paths override earlier ones by key, so app-level templates can replace shipped ones.

Single-section ("flat") template — one big canvas, no visible section header:

Multi-section template — each section has its own column count, GridStack row height, and optional visible header. row_span lets a section occupy several parent rows so asymmetric layouts fall out of CSS Grid auto-flow:

Template fields

Field Type Notes
key string Unique identifier. Stored on the dashboard as template_key.
name string Translation key used by __() for the template's display name.
description string Translation key used by __() (helper text in the template select).
columns int (1–24) Parent CSS Grid column count.
sections[] array 1 or more sections; rendered in source order via CSS Grid auto-flow.

Section fields

Field Type Notes
slug string Unique inside the template. Stored on widgets as section_slug.
name string or null Translation key for the visible header. null ⇒ no header.
columns int (1–parent) Both the section's column-span in the parent grid AND its inner GridStack columns.
row_span int (≥1, default 1) Number of parent rows the section spans. Use for asymmetric layouts.
row_height int (20–500) Inner GridStack cellHeight in pixels.

Translation keys

name and description on the template and name on each section are Laravel translation keys, not display strings. Lookup happens at render time via __(), so locale switches work without reloading templates. Missing keys fall back to the raw key string — useful as a "you forgot to translate this" signal.

Shipped translations live at resources/lang/{locale}/templates.php:

For your own templates, drop the JSON in your configured path and ship the matching translation strings in your app's lang/*/<file>.php.

Shipped presets

Key Display name Sections
flat-12 Standard 1 — main (12c)
2-columns Split left (6) + right (6)
3-columns Trio left (4) + middle (4) + right (4)
4-cells-2-rows Quad top-left (6) + top-right (6) + bottom-left (6) + bottom-right (6)
sidebar-main Sidebar sidebar (4) + main (8)
header-2cols-footer Report header (12) + left (6) + right (6) + footer (12)
2-left-1-right Showcase top-left (6) + right (6, row_span 2) + bottom-left (6)
kpi-strip-chart KPI kpi (12) + chart (12)

Each preset also ships an SVG thumbnail alongside its JSON (same filename, .svg extension) — useful if you want to render a visual preview in your own UI via app(TemplateRegistry::class)->previewSvg($key).

Custom paths and disabling templates

Add your own template directories via template_paths:

To hide some shipped templates from the manager's selector — without breaking dashboards that already reference them — use disabled_templates:

This is UI-only: TemplateRegistry::find() and default() still resolve disabled keys for already-existing dashboards.

Fallback behavior

Drag, Resize, and Cross-Section Moves

The dashboard renders as a CSS Grid of sections; each section is its own GridStack instance and all are linked, so users can drag widgets within a section AND between sections. Resize handles live at the bottom-right corner of each widget.

Interaction What persists
Drag within section x, y on the widget.
Drag to another section section_slug, plus new x, y at the drop target.
Resize from the corner w and/or h, clamped by the widget class's min/max.
Page refresh GridStack reads gs-x/y/w/h attributes back from HTML.

During a drag, every peer section's grid is highlighted with a soft blue background and dashed outline; the section that owns the dragged widget is highlighted more strongly. After the drop (or a cancel), highlights disappear immediately.

Locked dashboards and read-only users

When a dashboard's is_locked toggle is on, or the current user's canEdit() returns false:

The widgets still render normally, so non-editors see a clean read-only view.

Managing Filters

Defining filters

Override getDashboardFilters() to return an array of Filament Field components:

Per-dashboard filter session

Each dashboard stores its filters independently in the session (keyed by page class + dashboard id). Switching dashboards restores the last-used filters for that dashboard.

Per-dashboard filter visibility

Admins toggle which filters are visible for each dashboard from the Visible filters tab in the dashboard manager.

Per-dashboard default values

Default filter values are stored in the dashboard's filters JSON column. They're applied on first visit and when the user clicks the reset button.

Custom default-value fields

Override getDefaultFilterSchema() to provide alternative field types for editing defaults — for example a relative period selector instead of a date picker:

Filters not present in this array fall back to their original component from getDashboardFilters().

Resolving defaults at apply time

Override resolveFilterDefaults() to transform stored defaults into actual filter values:

Accessing filters in widgets

Use Filament's InteractsWithPageFilters trait:

The widget receives the active filter values as $this->pageFilters at mount, and the dashboard keeps them in sync automatically when the user edits a filter or resets the filter bar — no extra wiring needed.

Resetting filters

The filter bar includes a reset button. Clicking it calls resetFilters() on the page, which re-applies the dashboard's stored defaults (or clears filters if none are configured).

Dashboard User Interface

Dashboard selector

A dropdown button in the page header lets users switch between dashboards. The current dashboard is highlighted with a check icon. A Manage dashboards entry — visible to editors — opens the management slideover.

When the user has both global and personal dashboards visible, the dropdown groups them: globals first, then a visual separator, then personal entries — each prefixed with a user icon. The current dashboard always wins the check icon, even when it's personal.

Add Widget

The Add Widget button (visible to editors on unlocked dashboards) opens a modal with:

Size and position are deliberately absent — width and height come from the widget class's static methods, and (x, y) are managed visually by GridStack on the canvas. New widgets land at the bottom of the chosen section at the widget class's default size.

Widget wrapper

Each widget is wrapped with chrome that adds:

Dashboard Manager slideover

A reorderable table of all dashboards. Personal dashboards display a small user icon in the Name column; other users' personal dashboards are not listed at all — the manager only shows globals plus the viewer's own personals.

There is no template-management tab — templates are JSON files, edited on disk (or just shipped as presets).

Safety guards

Permissions & Authorization

canEdit()

Override canEdit() to restrict who can manage dashboards and widgets. When it returns false, the Add Widget button, the widget edit/delete icons, and the Manage dashboards entry are all hidden.

canDisplay()

Override canDisplay() to control per-dashboard visibility. The default logic is:

  1. Editors (canEdit() === true) always see every dashboard.
  2. If the dashboard model has Spatie roles, check $user->hasAnyRole($dashboard->roles).
  3. Fall back to the page-level canAccess().

Spatie Permission integration

  1. Set use_spatie_permissions to true in the config.
  2. The DashboardWithRoles model is automatically swapped in (it extends Dashboard and adds the HasRoles trait).
  3. A Roles multi-select appears in the dashboard manager form.
  4. canDisplay() checks $user->hasAnyRole($dashboard->roles) whenever roles are assigned.

Personal dashboards

Toggle Personal dashboard in the dashboard edit form to mark a dashboard as personal. Personal dashboards are scoped to their creator — even users for whom canEdit() returns true cannot see another user's personal dashboard in the picker, the manager table, or via a direct URL (canDisplay() short-circuits to false for non-owners). Globals continue to follow the existing canDisplay() / Spatie role logic.

Each dashboard also persists a created_by column (nullable foreign key to your users table, resolved from config('auth.providers.users.model')). The creator is captured automatically on creating; the Duplicate action re-assigns the new copy to the user who duplicated it.

When a user is deleted, the package's User::deleting hook deletes that user's personal dashboards. Global dashboards they created keep their row and have created_by set to null (audit residue rather than data loss).

Set the default for new dashboards with:

Configuration

Key Type Default Description
template_paths array [resource_path('dashboard-templates')] Extra directories scanned for layout JSON templates. The package's preset dir is always loaded first; user paths override on matching key.
default_template string 'flat-12' Template key used when a dashboard has none set, and as fallback when a referenced key is missing.
disabled_templates array [] Template keys to hide from the manager's selector. Dashboards already pointing at a disabled template keep rendering — UI-only filter.
use_spatie_permissions bool false Enable Spatie role integration.
default_personal bool false When true, the Personal dashboard toggle in the create form defaults to on. Existing dashboards are unaffected.

Translations

Supported languages: English (en), French (fr), Spanish (es), Portuguese (pt), German (de), Russian (ru), Chinese (zh), Bulgarian (bg), Croatian (hr), Danish (da), Estonian (et), Finnish (fi), Greek (el), Hungarian (hu), Italian (it), Dutch (nl), Polish (pl), Romanian (ro), Swedish (sv), Czech (cs), Japanese (ja), Arabic (ar), Turkish (tr).

Publish translations to customize them:

All UI translation keys are namespaced under filament-dynamic-dashboard::dashboard.*; template name/description keys are under filament-dynamic-dashboard::templates.*.

Changelog

See CHANGELOG.md for release notes.

Credits

License

The MIT License (MIT). See LICENSE.md for details.


All versions of filament-dynamic-dashboard with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
filament/filament Version ^4.1.1|^5.0
spatie/laravel-package-tools Version ^1.93
illuminate/contracts Version ^11.0|^12.0|^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package mddev31/filament-dynamic-dashboard contains the following files

Loading the files please wait ...