Performance

The Hidden Cost of Custom Admin Grids and Reports

Custom admin grids and reports load huge datasets with no cache to hide behind. Here is why they get slow and how to fix them without losing function.

Jason Schuman · March 28, 2026

Custom admin grids and reports have a hidden cost

Custom admin grids and reports are easy to build and easy to make slow. A grid that loads a large dataset, or a report that aggregates years of data live, can take seconds to render and strain the database each time.

Because the admin has no full-page cache, that cost is paid in full on every view. Staff wait, the database works hard, and a report nobody optimized becomes a recurring drag on the whole store.

This article covers why custom grids and reports get slow, the patterns behind the worst ones, and how to make them fast without losing their function.

The unpaginated collection

The most common mistake is loading everything at once. A grid or report that pulls a full collection into memory, rather than paginating, scales with the data until it eventually times out.

On a small dataset this works fine, which is why it ships. As the catalog or order history grows, the same code loads more and more, until the grid that was instant in testing crawls in production.

Pagination is the fix, loading only the rows a page shows. A grid should never load a hundred thousand rows to display fifty.

Filtering on unindexed columns

Grid filters generate queries, and a filter on an unindexed column forces a full table scan. Every time a user filters or sorts on that column, the database reads the whole table.

A slow custom grid is almost always a slow query. The grid loads a large collection or filters on an unindexed column, and the admin waits on the database exactly as a slow storefront page would.

The fix is the same as for any slow query: an index on the columns being filtered and sorted. Profiling the query behind a slow grid usually points straight at a missing index.

Heavy joins in the collection

Custom grids often join several tables to show related data in one view. Each join adds work, and a grid that joins many tables, especially large ones, builds an expensive query.

Sometimes the joins pull in data the grid does not even display, added during development and never removed. Trimming the collection to only what the grid shows can significantly reduce the query cost.

Reading the actual query a grid generates is the way to find this. A collection with joins to tables whose columns never appear in the grid is doing work for nothing.

Reports that aggregate live

Reports are grids' heavier cousins. A report that aggregates orders or sales across a long history runs a large grouping query every time it loads.

On years of data, that aggregation is expensive, and running it live means paying the cost on every view. A report that summarizes lifetime sales is asking the database to scan and group an enormous set on demand.

The answer is often to precompute. Aggregating on a schedule into a summary table, then reading the summary, turns a heavy live query into a fast lookup.

The memory cost of large reports

Reports that build large result sets in memory can exhaust the PHP memory limit. A report gathering a big dataset into an array, especially for export, can hit the fatal memory error mid-run.

This ties admin reports to memory exhaustion. A report that works on a year of data and fails on five years is outgrowing its memory allocation, and raising the limit only delays the next failure.

Processing in batches, streaming output rather than building it all in memory, is the durable fix. It keeps the report's memory use flat regardless of the data size.

Exports that time out

Exporting a large grid is where these problems combine. A synchronous export loads the full dataset, holds it in memory, and tries to produce a file before the request times out.

On a large export, that request can exceed the web server timeout and fail, sometimes after doing most of the work. The user sees an error and retries, doubling the load.

Asynchronous export solves this by moving the work to a background process that produces the file without a waiting request. It is the right pattern for any export large enough to risk a timeout.

Fixing without losing function

The goal is a fast grid or report that still does its job. Pagination, indexes, trimmed collections, precomputed aggregates, and asynchronous exports each address a specific cause without removing the feature.

The order to apply them follows the profiling. Read the query the grid or report generates, find whether the cost is the collection size, the filters, the joins, or the aggregation, and fix that specific cause.

This is the same query-first discipline that fixes storefront performance, applied to the admin. The admin just makes the cost more visible, because nothing caches it.

Fast tools for the people who run the store

Custom admin grids and reports are staff tools, and slow ones cost staff time every day. Their performance is a query problem, and it responds to the same fixes as any other slow query.

Knowing which of your custom grids and reports are slow, and why, turns a daily frustration into a short optimization list. Profiling those admin tools is a practical part of a performance review.