demency/nova-gridder
Nova Gridder adds a flexible grid field to Laravel Nova, letting you arrange multiple Nova fields into responsive columns for cleaner, more compact resource forms and detail views. Configure layouts directly in your Nova resources.
Confirm compatibility before you composer require. The package targets Laravel Nova and was last tagged in mid-2021, so check the composer.json constraint against your installed Nova major (nova/laravel/nova). On a Nova 4/5 app you should treat this as a "vendor and verify" step, not a drop-in.
composer require demency/nova-gridder
After install, look at three things in the repo before writing any layout code:
README.md — the canonical usage example. With 17 stars and one release, this is the documentation.src/ — the field class(es) exposed (the namespace is Demency\NovaGridder) and their fluent setters. The public methods are the real API.resources/js/components/ — the Vue component that renders the grid. This tells you exactly which Tailwind grid utilities are honoured and how responsiveness is wired.First use case: take a Resource detail view that currently stacks 8–12 fields vertically and lay them out as a responsive grid on the details page.
use Demency\NovaGridder\NovaGridder;
public function fields(NovaRequest $request)
{
return [
NovaGridder::make()
->fields([
ID::make()->sortable(),
Text::make('Name'),
Text::make('Email'),
Text::make('Company'),
]),
];
}
Start with a small, read-only resource (a "show" page you rarely edit), verify rendering, then roll it out to resources with more complex field mixes. Do not start with a resource that uses custom Field subclasses or file uploads — get the layout mechanics working on plain fields first.
Wrap only the detail-relevant fields. The grid is a layout container; fields you want on index/forms should stay in the normal fields() array. A common pattern is to keep the flat fields used by forms/index and add the grid container for the detail context, or split the array based on $request->isResourceDetailRequest() (or the equivalent isDetail()/isForm() helpers your Nova version provides).
Responsive column strategy. Because the package is built on Tailwind's grid, express the layout as a mobile-first scale and let the component/your config map it to sm:/md:/lg:/xl: breakpoints. A practical default for Nova detail pages is one column on mobile, two at md, three at xl. Keep the total number of columns in sync with any span/offset values you assign.
Span wide fields. Text-heavy fields (description, notes, JSON, code) usually want to span multiple columns while scalars sit one-per-cell. Use whatever span/width setter the field exposes (check src/ — many grid packages on Nova use ->span(), ->colSpan(), or a Tailwind class string). If spans aren't available, restructure with multiple grid rows instead of fighting the API.
Extract layouts for reuse. Don't repeat grid definitions in every resource. Put the layout in a trait or abstract resource:
trait HasDetailLayout
{
protected function detailGrid(): NovaGridder
{
return NovaGridder::make()->fields([
$this->identityFields(),
$this->contactFields(),
$this->auditFields(),
]);
}
}
Fields are still plain Laravel\Nova\Fields\Field instances, so ->canSee(), ->hideWhenCreating(), ->readonly(), and the rest continue to work inside the grid.
Group by meaning, not by column count. The temptation is to arrange fields so the grid "looks right." Arrange by domain instead — identity, contact, metadata — and accept some asymmetry. Grids that mirror the data model survive refactors; grids tuned to a screenshot don't.
Conditional layout per request. Layout can legitimately vary by user role. Wrap the grid construction in a Gate::allows()/policy check and return either the grid or a plain field array. Keep this logic in the resource, not in the field container — it's easier to test.
Pair with Panel sparingly. If your Nova version supports Panel, a panel containing a gridder (or a gridder containing panels) gets visually noisy fast. Pick one level of nesting: panels for section headings, grid for the fields inside a panel.
canSee() returning false means the field isn't rendered, so a grid row can end up with fewer cells than columns. Design for graceful fallback (e.g. don't hard-code a single field into a 4-column row and assume it fills the width).Form/Detail split, Vue 3, etc.). A clean composer update isn't proof it renders — actually open the detail page.vendor:publish, re-run npm run prod (or npm run dev) so the Vue component is compiled into public/vendor/.... A silent 404 for the JS chunk shows as an empty or broken panel with no obvious error.src/ to see how props are populated server-side. Most "it does nothing" reports are a missing asset publish, not a layout bug.Field with its own Vue component for the one-off widget. Don't fork the package for a single layout tweak — vendor it locally (composer.json path repository) first if you must.Panel/Field grouping, or a more actively maintained grid package, covers the same need with less vendor risk.How can I help you explore Laravel packages today?