Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laragrid Laravel Package

bored-programmers/laragrid

Laragrid is a Laravel package for building interactive data grids and tables with minimal effort. It handles sorting, filtering, pagination, and searchable columns, letting you render responsive, customizable grids from Eloquent models or query builders while keeping your controllers and Blade views...

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and pin.

composer require bored-programmers/laragrid:^1.2

Pin to a minor version deliberately. v1.2.7 shipped with a compare-link-only changelog, so treat upgrades as "read the diff" events rather than trusting semver. Before the first commit, confirm the package's composer.json constraints against your Laravel/PHP matrix — this is a 2023-era package and the last tagged release predates several Laravel majors.

2. Read these three things before writing code (in this order):

  • The README's quickstart — the fluent grid/column API is the entire value proposition, and method names are the thing you'll be typing all day.
  • src/ — specifically the abstract grid/base class and the column class. You need to know the method names for column definition, filter registration, and rendering, plus whatever query-builder seam the package exposes. Skim it for how it composes WHERE/ORDER BY from request input; that's the path your security review should focus on.
  • config/ (and php artisan vendor:publish --tag=laragrid-config if available) — pagination defaults, view paths, and any escaping/rendering flags live here.

3. First use case: one internal read-only list screen. Pick your least-risky list — an orders queue, an audit log, a customers index. Do not start with a multi-tenant, permission-scoped screen. The goal of the first screen is to learn the API's shape and measure the build delta against a hand-rolled Blade table.

4. Minimum viable grid. The shape is typically: a grid class declaring columns, a controller action returning a view, and a Blade template rendering the grid. Illustrative — verify exact method names against the source:

// app/Grids/OrdersGrid.php
class OrdersGrid extends Grid
{
    public function columns(): array
    {
        return [
            Column::make('id')->sortable(),
            Column::make('customer.name')->label('Customer')->searchable(),
            Column::make('status')->filterable([...statuses]),
            Column::make('total')->label('Total'),
            DateColumn::make('created_at')->sortable(),
        ];
    }

    public function query(): Builder
    {
        return Order::query()->with('customer'); // eager load early
    }
}
// Controller
public function index(OrdersGrid $grid)
{
    return view('orders.index', ['grid' => $grid]);
}
{{-- orders/index.blade.php --}}
@extends('layouts.app')
@section('content')
    {!! $grid->render() !!}   {{-- or @include of the package view; check README --}}
@endsection

Get it rendering with data first. Then add sorting, then one filter, then row actions. Adding all three at once makes it harder to tell whether a broken screen is your query or the package's request handling.

Implementation Patterns

One grid class per screen, grouped by domain. app/Grids/ with a class per list view keeps the column/filter declarations reviewable in isolation and makes the grid the natural place for the query. Resist a single god-grid with conditional columns — you'll end up with if ($user->isAdmin()) scattered through column definitions and lose the readability the package is supposed to buy you.

Treat the grid class as a presenter, not a model. Keep business logic in Eloquent scopes and query objects; the grid class should declare columns and hand off to a query. That split is also your exit path — if you ever swap the package out, the scopes survive.

Standardize the boring columns once. Most teams immediately want an id, created_at, and an actions column on everything. Either subclass a BaseAdminGrid with those defaults baked in, or define reusable column factory helpers. This is where the cycle-time savings compound — the tenth screen should be ~20 lines.

Filtering: prefer package filters over ad-hoc where calls, but validate everything. Any filter that pulls from request() is an input surface. Whitelist filterable columns and allowed operators explicitly. If the package accepts a column name from the query string and interpolates it into an orderBy, a sortable-column allowlist is mandatory — this is the single highest-value review you can do before adoption, and the package's tests are the place to confirm the guard exists.

Row actions: keep authorization in the grid, not the Blade view.

Action::make('edit')
    ->url(fn (Order $o) => route('orders.edit', $o))
    ->visible(fn (Order $o) => Gate::allows('update', $o));

Hiding a button is not authorization — the route must still check policy. But showing an action the user will be 403'd on is a support ticket, so gate visibility in the grid and enforce in the controller.

Custom column rendering via Blade partials is the pattern for anything non-scalar: status badges, avatar+customer name stacks, inline progress bars. Keep the partial dumb (receives the row, outputs markup) so it's unit-testable and reusable outside the grid.

Eager loading is your responsibility, not the package's. The grid will happily lazy-load a relation per row. ->with() in the grid's query method, and check DB::getQueryLog() or Laravel Telescope on the first render of every new grid. This is the most common cause of "the grid is slow" complaints.

Filters belong in the query string. Make sure filter and sort state survives page navigation and is shareable/bookmarkable. If the package doesn't persist state in the URL, add it at the controller level by appending to pagination links — otherwise users lose their filter on page 2 and file a bug.

Testing: assert the HTTP endpoint, not the grid internals. Feature tests that hit /orders?sort=total&dir=desc&status=paid and assert the visible rows and ordering are the right level of granularity — they cover the package's query composition and your column config in one shot. Add one test per filter that asserts the filter actually narrows results; filters are the most likely thing to silently no-op.

Integration with existing UI: the package renders a table, not your design system. Plan on overriding the package views (publish them if possible) to match your layout, or wrap the grid output in your own card/header/toolbar markup. Budget a half-day on the first screen for visual alignment; subsequent screens are cheap.

Gotchas and Tips

The changelog is empty — pin and diff. v1.2.7 is a patch release with nothing but an auto-generated compare link. You cannot characterize what changed. Pin ^1.2 (or an exact patch), subscribe to repo releases, and read git diff before bumping. Do not put this on a critical path where a surprise breaking change becomes an incident.

Maintenance health is unverified. One patch release, three stars, an org-style handle. That's not disqualifying for a convenience layer over Eloquent + Blade, but it means: no assumed CVE process, no assumed LTS cadence. Keep the abstraction contained to list screens so the migration cost if it stalls is bounded (a weekend of reimplementing tables, not a quarter).

Sorting/filtering from request input is the security hot spot. Two things to confirm in the source before this touches tenant-scoped data:

  • Column names used in orderBy/where come from an allowlist, not raw request input.
  • The base query respects your global scopes (multi-tenancy, soft deletes). If the package builds its own DB::table() instead of using your Eloquent builder, global scopes will be bypassed silently.

Write an explicit test: log in as Tenant A, request Tenant B's record via a filter/sort param, assert 404/empty.

Aggregates break pagination and sorting. If you need withCount, groupBy, or having (e.g. "orders with 3+ items"), check how the package computes the total count and applies LIMIT. Naive paginate() on grouped queries returns wrong totals. Test with a dataset that crosses a page boundary.

Escaping in custom columns. Any hand-written column closure or partial that interpolates user-controlled data is an XSS vector. Use {{ }} in partials; only reach for {!! !!} when you're rendering markup you generated. Verify whether the package escapes scalar column output by default or trusts you — this differs between packages and there's no changelog to tell you.

Server-rendered means full page loads. Every sort, filter, and page change is a round trip. That's fine for internal screens; it's wrong for anything where users expect instant feedback, column resizing, inline editing, or virtualization. Agree this constraint with design before adoption rather than retrofitting a JS grid mid-sprint.

Empty states and error states. The default "no records" row is usually ugly and often wrong (it fires on filtered-to-zero, not just empty tables). Override it, and differentiate "no data yet" from "no matches for these filters" — the second should offer a clear-filters link.

Accessibility and i18n are on you. No signal that the package ships ARIA-sortable headers, keyboard-navigable pagination, or translatable labels. If those are obligations, audit the rendered markup on the first screen and budget override work; don't discover it at design review.

Config quirks to check on install: default per-page and available page sizes, the view namespace you'll override, whether pagination uses Bootstrap or Tailwind markup (mismatched CSS is the classic day-one surprise), and any caching of query results.

Extension points worth knowing: the base grid class (for shared columns/query), column classes (subclass for custom renderers), published views (for markup), and any query callback seam (for injecting scopes). If the package has no seam for injecting a query modification, that's a red flag — you'll end up copy-pasting where clauses.

Debugging checklist when a grid misbehaves: (1) toSql() / getBindings() on the grid's query, (2) DB::getQueryLog() for N+1, (3) dump the request's sort/filter params to confirm they reach the grid, (4) confirm the column key matches the actual DB column or relation path — a typo'd column often fails silently rather than throwing.

Keep an exit hatch. Because the grid wraps idiomatic Eloquent and Blade, the swap cost is low if you keep column rendering in partials and query logic in scopes. Do that from screen one; it costs nothing and it's the difference between a contained convenience dependency and a migration project.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource