How to Build Dynamic, Filterable Charts with ApexCharts in Laravel 11
In this comprehensive Laravel 11 tutorial, you'll learn how to create dynamic charts with filter options using ApexCharts — a powerful, modern JavaScript charting library known for its beautiful visuals, interactivity, and responsiveness. We'll build a real-world example: an interactive monthly user registration chart that updates in real-time based on a selected year via a simple dropdown filter and AJAX requests — no page reloads required.Why is this valuable? Static charts limit insights, especially in admin dashboards, analytics tools, or reporting systems. By adding dynamic filters, you enable users to drill down into data effortlessly (e.g., compare user growth year-over-year), improving decision-making and user experience. This approach combines Laravel's robust backend (Eloquent queries with groupBy and whereYear), secure AJAX handling (CSRF protection), and ApexCharts' flexible frontend rendering — resulting in professional, production-ready data visualization without heavy packages.
Whether you're building SaaS analytics, e-commerce reports, or internal tools, mastering dynamic ApexCharts in Laravel 11 unlocks scalable, engaging dashboards. By the end, you'll have a fully working demo ready to extend with more filters, chart types (bar, line, area), or even Livewire integration for even smoother reactivity.

Table Of Content
1 Prerequisites
- PHP 8.2 or higher
- Composer installed
- A database like MySQL or MariaDB
- Basic knowledge of Laravel, PHP, and JavaScript
We'll use Bootstrap for basic styling and jQuery for AJAX, but you can swap them with Tailwind or vanilla JS if preferred.
2 Introduction
In this step-by-step Laravel 11 tutorial, you’ll learn how to seamlessly integrate ApexCharts with dynamic filter options to create powerful, interactive data visualizations. We’ll guide you through generating dummy user records, then displaying a real-time updating line chart (or bar chart — your choice) that shows monthly user registrations for the selected year. Using a clean dropdown filter and AJAX, the chart refreshes instantly without reloading the page — perfect for modern admin dashboards, analytics panels, or reporting features.
By the end of this guide, you’ll have a fully functional, production-ready example of dynamic charts in Laravel 11 with ApexCharts, complete with proper Eloquent queries, secure AJAX handling, and responsive design. Whether you're building SaaS analytics, internal tools, or client-facing reports, this technique will help you deliver professional-grade data visualization with minimal effort.
3 Install a Fresh Laravel 11 Project
3.1 Install Laravel Project
composer create-project laravel/laravel laravel-apexcharts-demo
Navigate to your project directory:
cd laravel-apexcharts-demo
This sets up Laravel 11 with all necessary dependencies. 3.2 Configure MySql Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=charts
DB_USERNAME=root
DB_PASSWORD=
4 Create Migration
php artisan make:migration create_users_table
Edit the migration file to define the table structure, then run:
<?php
se Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
Run the migration:
php artisan migrate
5 Create Model
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
6 Seed Dummy User Data for Testing
php artisan make:factory UserFactory
Update the factory to generate sample data and seed the database:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => Hash::make('password'),
'remember_token' => Str::random(10),
'created_at' => fake()->dateTimeBetween('-2 years', 'now'),
'updated_at' => now(),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
To generate 200 dummy records, use:
php artisan tinker
>>> \App\Models\User::factory()->count(200)->create();
>>> exit
This populates your users table with testable data.
7 Create the Controller for Chart Data
php artisan make:controller ChartController
In the controller, implement index and filterData methods to populate the charts dynamically. This allows you to explore how to create dynamic charts with filter options in ApexCharts using Laravel 11.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class ChartController extends Controller
{
public function index()
{
$years = $this->getAvailableYears();
$defaultYear = now()->year;
$chartData = $this->getChartData($defaultYear);
return view('charts.index', compact('years', 'defaultYear', 'chartData'));
}
public function filter(Request $request)
{
$year = $request->input('year', now()->year);
$chartData = $this->getChartData($year);
return response()->json(['chartData' => $chartData]);
}
private function getChartData($year)
{
$data = User::select(
DB::raw('MONTH(created_at) as month'),
DB::raw('COUNT(*) as count')
)
->whereYear('created_at', $year)
->groupBy('month')
->orderBy('month')
->pluck('count', 'month')
->toArray();
// Fill missing months with 0
$series = [];
for ($month = 1; $month <= 12; $month++) {
$series[] = $data[$month] ?? 0;
}
return $series;
}
private function getAvailableYears()
{
// Dynamically get years from data
return User::selectRaw('YEAR(created_at) as year')
->distinct()
->orderBy('year', 'desc')
->pluck('year')
->toArray();
}
}
?>
This controller fetches monthly user counts for a given year and provides dynamic years based on your data.
8 Create the Blade View with ApexCharts and AJAX Filter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Charts with ApexCharts in Laravel 11</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- ApexCharts -->
<script src="https://cdn.jsdelivr.net/npm/apexcharts@latest"></script>
</head>
<body>
<div class="container mt-5">
<h1 class="mb-4">Monthly User Registrations Chart</h1>
<div class="card">
<div class="card-header">
<form id="filterForm" class="d-flex align-items-center">
<label for="year" class="me-2">Select Year:</label>
<select name="year" id="year" class="form-select w-auto me-2">
@foreach ($years as $y)
<option value="{{ $y }}" {{ $y == $defaultYear ? 'selected' : '' }}>{{ $y }}</option>
@endforeach
</select>
<button type="submit" class="btn btn-primary">Filter</button>
</form>
</div>
<div class="card-body">
<div id="chart"></div>
</div>
</div>
</div>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
var chartData = @json($chartData);
var options = {
series: [{
name: 'Users',
data: chartData
}],
chart: {
height: 350,
type: 'bar',
toolbar: { show: true }
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '55%',
endingShape: 'rounded'
},
},
dataLabels: { enabled: false },
stroke: { show: true, width: 2, colors: ['transparent'] },
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
yaxis: { title: { text: 'Number of Users' } },
fill: { opacity: 1 },
tooltip: { y: { formatter: val => val + " users" } },
colors: ['#008FFB']
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
// AJAX Filter
$('#filterForm').on('submit', function(e) {
e.preventDefault();
var year = $('#year').val();
$.ajax({
url: '{{ route('charts.filter') }}',
type: 'POST',
data: { year: year, _token: '{{ csrf_token() }}' },
success: function(response) {
chart.updateSeries([{ data: response.chartData }]);
},
error: function(xhr) {
console.error('Error:', xhr.responseText);
}
});
});
</script>
</body>
</html>
9 Define Routes
use App\Http\Controllers\ChartController;
use Illuminate\Support\Facades\Route;
Route::get('/charts', [ChartController::class, 'index'])->name('charts.index');
Route::post('/charts/filter', [ChartController::class, 'filter'])->name('charts.filter');
10 Folder Structure
11 Run Laravel Server to Test the App
php artisan serve
Visit http://127.0.0.1:8000/charts in your browser. Select a year from the dropdown and click "Filter" – the chart should update dynamically with monthly user counts.
Troubleshooting Tips
- No data? Ensure you seeded users and check your database.
- AJAX errors? Verify CSRF token and console for JS errors.
- Chart not rendering? Check CDN links and browser console.
- For production, add error handling in the controller (e.g., validate year input) and consider caching queries.
12 Conclusion
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
ApexCharts is a modern JavaScript charting library for creating interactive, responsive visualizations like bar, line, and area charts. In Laravel, it enables dynamic data dashboards without page reloads, using AJAX for real-time updates.
Include ApexCharts via CDN in your Blade view. Set up a controller to fetch data, render the chart with initial options, and use JavaScript to initialize the chart object.
Create a dropdown for filters (e.g., year selection). On change, use jQuery AJAX to POST the filter value to a Laravel route, fetch filtered data from the database, and update the chart dynamically with chart.updateSeries().
Use the ApexCharts exec() method or updateSeries() with new data returned from an AJAX call. This allows real-time chart refreshes based on user filters.
No, you can integrate directly via CDN and manual JavaScript. Packages like Larapex Charts or akaunting/laravel-apexcharts simplify PHP-based configuration but are optional for basic setups.
The tutorial demonstrates a bar chart (configured as 'bar' type) showing monthly user registrations, though it can be easily changed to line or area charts.
Include the CSRF token in your AJAX data: '_token': '{{ csrf_token() }}'. This ensures secure POST requests to filter routes.
Yes, query Eloquent models (e.g., User::whereYear()->groupBy(month)) to aggregate data like monthly counts, then format and return as JSON for the chart.
