diff --git a/packages/tables/resources/views/index.blade.php b/packages/tables/resources/views/index.blade.php
index 2fb3fb1e37..bb1b867e53 100644
--- a/packages/tables/resources/views/index.blade.php
+++ b/packages/tables/resources/views/index.blade.php
@@ -1301,7 +1301,7 @@
@endphp
@endforeach
- @if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && ((! $records instanceof \Illuminate\Contracts\Pagination\Paginator) || (! $records->hasMorePages())))
+ @if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && $this->shouldRenderTrailingGroupedTableSummary($previousRecord))
@php
@@ -2353,7 +2353,7 @@
@endphp
@endforeach
- @if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && ((! $records instanceof \Illuminate\Contracts\Pagination\Paginator) || (! $records->hasMorePages())))
+ @if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && $this->shouldRenderTrailingGroupedTableSummary($previousRecord))
@php
$groupColumn = $group->getColumn();
$groupScopedAllTableSummaryQuery = $group->scopeQuery($this->getAllTableSummaryQuery(), $previousRecord);
diff --git a/packages/tables/src/Concerns/CanSummarizeRecords.php b/packages/tables/src/Concerns/CanSummarizeRecords.php
index a53a9a8df4..6477e01a64 100644
--- a/packages/tables/src/Concerns/CanSummarizeRecords.php
+++ b/packages/tables/src/Concerns/CanSummarizeRecords.php
@@ -4,13 +4,68 @@ namespace Filament\Tables\Concerns;
use Closure;
use Filament\Support\Services\RelationshipJoiner;
+use Illuminate\Contracts\Pagination\CursorPaginator;
+use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Expression;
use Illuminate\Support\Str;
use stdClass;
trait CanSummarizeRecords
{
+ /**
+ * @param Model | array | null $lastRecord
+ */
+ public function shouldRenderTrailingGroupedTableSummary(Model | array | null $lastRecord): bool
+ {
+ if ($lastRecord === null) {
+ return false;
+ }
+
+ $records = $this->getTableRecords();
+
+ $isPaginated = ($records instanceof Paginator) || ($records instanceof CursorPaginator);
+
+ if ((! $isPaginated) || (! $records->hasMorePages())) {
+ return true;
+ }
+
+ $group = $this->getTableGrouping();
+
+ if (! $group) {
+ return true;
+ }
+
+ $query = $this->getFilteredSortedTableQuery();
+
+ if ($query === null) {
+ return true;
+ }
+
+ if ($records instanceof CursorPaginator) {
+ $nextCursor = $records->nextCursor();
+
+ if (! $nextCursor) {
+ return true;
+ }
+
+ $nextPageFirstRecord = (clone $query)
+ ->cursorPaginate(perPage: 1, cursor: $nextCursor)
+ ->items()[0] ?? null;
+ } else {
+ $nextPageFirstRecord = (clone $query)
+ ->skip($records->currentPage() * $records->perPage())
+ ->first();
+ }
+
+ if ($nextPageFirstRecord === null) {
+ return true;
+ }
+
+ return $group->getStringKey($nextPageFirstRecord) !== $group->getStringKey($lastRecord);
+ }
+
public function getAllTableSummaryQuery(): ?Builder
{
return $this->getFilteredTableQuery();
diff --git a/tests/src/Fixtures/Livewire/PostsTableWithCursorPagination.php b/tests/src/Fixtures/Livewire/PostsTableWithCursorPagination.php
new file mode 100644
index 0000000000..22c0bb5320
--- /dev/null
+++ b/tests/src/Fixtures/Livewire/PostsTableWithCursorPagination.php
@@ -0,0 +1,44 @@
+query(Post::query())
+ ->paginationMode(PaginationMode::Cursor)
+ ->groups(fn () => [
+ Tables\Grouping\Group::make('title'),
+ ])
+ ->columns([
+ Tables\Columns\TextColumn::make('title')
+ ->sortable(),
+ Tables\Columns\TextColumn::make('rating')
+ ->summarize([
+ Tables\Columns\Summarizers\Sum::make('sum'),
+ ]),
+ ]);
+ }
+
+ public function render(): View
+ {
+ return view('livewire.table');
+ }
+}
diff --git a/tests/src/Tables/SummaryTest.php b/tests/src/Tables/SummaryTest.php
index ace4b66f86..93a1a4dbf2 100644
--- a/tests/src/Tables/SummaryTest.php
+++ b/tests/src/Tables/SummaryTest.php
@@ -7,10 +7,10 @@ use Filament\Schemas\Contracts\HasSchemas;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Tests\Fixtures\Livewire\PostsTable;
+use Filament\Tests\Fixtures\Livewire\PostsTableWithCursorPagination;
use Filament\Tests\Fixtures\Models\Post;
use Filament\Tests\Tables\TestCase;
use Illuminate\Contracts\View\View;
-use Illuminate\Database\Query\Builder;
use Livewire\Component;
use function Filament\Tests\livewire;
@@ -169,6 +169,46 @@ it('renders group summaries when page and all-table summaries are disabled', fun
->assertDontSeeHtml('fi-ta-summary-header-row');
});
+it('renders the trailing group summary when the next page starts a different group', function (): void {
+ Post::factory()->count(10)->create(['title' => 'A']);
+ Post::factory()->create(['title' => 'B']);
+
+ livewire(PostsTable::class)
+ ->set('tableRecordsPerPage', 10)
+ ->set('tableGrouping', 'title')
+ ->assertSee('A summary')
+ ->assertDontSee('B summary');
+});
+
+it('does not render the trailing group summary when the next page continues the same group', function (): void {
+ Post::factory()->count(15)->create(['title' => 'A']);
+
+ livewire(PostsTable::class)
+ ->set('tableRecordsPerPage', 10)
+ ->set('tableGrouping', 'title')
+ ->assertDontSee('A summary');
+});
+
+it('renders the trailing group summary with cursor pagination when the next page starts a different group', function (): void {
+ Post::factory()->count(10)->create(['title' => 'A']);
+ Post::factory()->create(['title' => 'B']);
+
+ livewire(PostsTableWithCursorPagination::class)
+ ->set('tableRecordsPerPage', 10)
+ ->set('tableGrouping', 'title')
+ ->assertSee('A summary')
+ ->assertDontSee('B summary');
+});
+
+it('does not render the trailing group summary with cursor pagination when the next page continues the same group', function (): void {
+ Post::factory()->count(15)->create(['title' => 'A']);
+
+ livewire(PostsTableWithCursorPagination::class)
+ ->set('tableRecordsPerPage', 10)
+ ->set('tableGrouping', 'title')
+ ->assertDontSee('A summary');
+});
+
class TestTableWithGroupSummariesOnly extends Component implements HasActions, HasSchemas, Tables\Contracts\HasTable
{
use InteractsWithActions;