1. Go to this page and download the library: Download asharif88/filament-plotly library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
asharif88 / filament-plotly example snippets
use Asharif88\FilamentPlotly\FilamentPlotlyPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
FilamentPlotlyPlugin::make()
]);
}
protected function isCollapsible(): bool
{
return true;
}
protected static ?int $contentHeight = 400; //px
protected function getContentHeight(): ?int
{
return 400;
}
protected static ?string $footer = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.';
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;
protected function getFooter(): null|string|Htmlable|View
{
return view('custom-footer', ['text' => 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.']);
}
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;
protected function getFooter(): null|string|Htmlable|View
{
return new HtmlString('<p class="text-danger-500">Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>');
}
use Filament\Actions\Action;
use Filament\Support\Enums\Alignment;
protected function getHeaderActions(): array
{
return [
Action::make('refresh')
->label('Refresh')
->icon('heroicon-o-refresh')
->action('updateOptions')
->button(),
Action::make('download')
->label('Download')
->url(route('reports.export'))
->color('secondary'),
];
}
protected function getHeaderActionsAlignment(): ?Alignment
{
return Alignment::End; // align header actions to the right
}
use Filament\Actions\Action;
use Filament\Support\Enums\Alignment;
protected function getFooterActions(): array
{
return [
Action::make('details')
->label('Details')
->url(route('reports.details'))
->button(),
];
}
protected function getFooterActionsAlignment(): ?Alignment
{
return Alignment::Center; // center footer actions
}
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
use Filament\Widgets\ChartWidget\Concerns\HasFiltersSchema;
use Asharif88\FilamentPlotly\Widgets\PlotlyWidget;
class BlogPostsChart extends PlotlyWidget
{
use HasFiltersSchema;
public function filtersSchema(Schema $schema): Schema
{
return $schema->components([
TextInput::make('title')
->default('Blog Posts Chart'),
DatePicker::make('date_start')
->default('2025-07-01'),
DatePicker::make('date_end')
->default('2025-07-31'),
]);
}
/**
* Use this method to update the chart options when the filter form is submitted.
*/
public function updatedInteractsWithSchemas(string $statePath): void
{
$this->updateOptions();
}
}
use Illuminate\Contracts\View\View;
protected function getLoadingIndicator(): null|string|View
{
return view('custom-loading-indicator');
}
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;
protected function getChartOverlay(): null|string|Htmlable|View
{
return view('charts.my-overlay');
}
protected function getOnChartReadyScript(): ?string
{
return <<<'JS'
// `el` is the Plotly chart DOM element
el.on('plotly_afterplot', () => {
document.getElementById('stream-progress').style.display = 'none';
});
JS;
}
use Asharif88\FilamentPlotly\Concerns\HasChartTheme;
use Asharif88\FilamentPlotly\Widgets\PlotlyWidget;
class RevenueChart extends PlotlyWidget
{
use HasChartTheme;
protected function getDarkThemeLayout(): array
{
return [
'paper_bgcolor' => '#1e293b',
'plot_bgcolor' => '#1e293b',
'font' => ['color' => '#f1f5f9'],
];
}
protected function getLightThemeLayout(): array
{
return [
'paper_bgcolor' => '#ffffff',
'plot_bgcolor' => '#ffffff',
'font' => ['color' => '#0f172a'],
];
}
}
protected function getPlotlyEventListeners(): array
{
return [
'plotly_click' => 'onChartClick',
'plotly_selected' => 'onChartSelected',
];
}
public function onChartClick(array $data): void
{
$recordId = $data['points'][0]['customdata'] ?? null;
$this->dispatch('open-record', id: $recordId);
}
public function onChartSelected(array $data): void
{
$ids = collect($data['points'])->pluck('customdata')->filter()->all();
$this->dispatch('filter-table', ids: $ids);
}
protected function getChartData(): array
{
$rows = Order::query()->get();
return [[
'x' => $rows->pluck('created_at'),
'y' => $rows->pluck('total'),
'customdata' => $rows->pluck('id'), // ← record IDs travel with each point
'type' => 'scatter',
'mode' => 'markers',
]];
}
use Livewire\Attributes\On;
#[On('plotly:plotly_click')]
public function handleChartClick(array $data): void
{
$this->tableFilters['id'] = $data['points'][0]['customdata'];
$this->resetTable();
}
use Asharif88\FilamentPlotly\Concerns\HasStreamingSupport;
use Asharif88\FilamentPlotly\Widgets\PlotlyWidget;
class LiveDataChart extends PlotlyWidget
{
use HasStreamingSupport;
public int $sourceId = 1;
// Return null to disable streaming (e.g. before $this->sourceId];
}
// JS body called for each data message. `d` = parsed JSON, `el` = chart DOM element.
// Return null to use the built-in default: Plotly.extendTraces(el, {x:[[d.x]], y:[[d.y]]}, [0])
protected function getOnStreamMessageScript(): ?string
{
return <<<'JS'
Plotly.extendTraces(el, { x: [[d.ts]], y: [[d.value]] }, [0]);
JS;
}
// JS body called once when {done: true} arrives, after the overlay is removed.
// `el` is the chart DOM element. Return null if no post-stream work is needed.
protected function getOnStreamDoneScript(): ?string
{
return null;
}
protected function getChartData(): array
{
return [['x' => [], 'y' => [], 'type' => 'scatter', 'mode' => 'lines']];
}
}