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

Nova Tabs Laravel Package

marshmallow/nova-tabs

Organize Laravel Nova resource forms into clean, navigable tabs. This package lets you group fields into tabbed panels, keeping complex forms tidy and improving the editing experience for admins and users alike.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: this package has no published documentation reachable from the repo listing, so treat it as a reference implementation rather than a fully documented dependency. Budget ~30 minutes in src/ before composer require. The v2.31.0 release (a merged community PR fixing PHP 8.4 deprecations) confirms two things the earlier assessment got wrong: the package is on a long 2.x release line (this bump is v2.30.0 → v2.31.0, not a first release), and it is not fully archived — an outside contributor (@stefvanesch) had a PR merged. It is still very low-traffic with no docs, so plan for reading source rather than searching for answers.

Prerequisites:

  • Laravel Nova 4.x or 5.x — verify the require block on laravel/nova in composer.json for the exact tag you install.
  • PHP 8.1+; PHP 8.4 is now supported without deprecation noise as of v2.31.0.

Minimal steps:

composer require marshmallow-packages/nova-tabs:^2.31

Nova field packages usually ship a service provider auto-discovered via extra.laravel.providers. If it ships JS/CSS, run:

php artisan vendor:publish --tag=nova-tabs-assets
php artisan nova:assets

Where to look first, in this order:

  1. composer.json — the require constraint on laravel/nova tells you instantly whether it will install against your Nova version. Also check whether the 2.x line has moved its minimum PHP beyond yours.
  2. src/ — identify the field class (usually NovaTabs, Tabs, or Tab). The fields() / addTab() / withToolbar() methods define the entire public API. There is no separate docs surface worth hunting for.
  3. resources/js/ — confirms whether it renders client-side (JS component) or is pure PHP layout sugar. This determines whether nova:assets matters.
  4. README.md — if present, it is the only usage reference; expect it to be a single code block.

First use case — grouping a bloated fields() method into logical tabs on a Resource:

use Marshmallow\NovaTabs\NovaTabs;

public function fields(NovaRequest $request)
{
    return [
        NovaTabs::make('Details')
            ->addTab('General', [
                ID::make()->sortable(),
                Text::make('Name')->rules('required'),
            ])
            ->addTab('Content', [
                Textarea::make('Body'),
                Markdown::make('Notes'),
            ])
            ->addTab('SEO', [
                Text::make('Meta Title'),
                Text::make('Meta Description'),
            ]),
    ];
}

Verify the exact method names against src/ before committing — forks of tab packages have diverged (addTab vs tab vs ->tabs([...])), and a 2.x major bump may have changed them relative to older blog posts you find.

Implementation Patterns

Tab-per-concern on large resources. The core value is keeping fields() readable and the form scannable. A workable heuristic: any resource with more than ~15 fields or more than two Panel groups is a tabs candidate. Group by audience (Editor / SEO / Publishing) rather than by column type — that is what actually reduces cognitive load for content editors.

Conditional tabs. Almost every tabs package exposes canSee() on the tab object, because tabs are fields under the hood. Use it to hide irrelevant tabs per-request:

NovaTabs::make('Details')
    ->addTab('Publishing', [...])->canSee(fn ($request) => $request->user()->can('publish'))
    ->addTab('Advanced', [...])->canSee(fn ($request) => $request->user()->isAdmin())

If the package does not support per-tab canSee, wrap the whole NovaTabs field instead.

Detail view reuse. Tabs are typically a field container, so the same instance can be dropped into fields() for both index-detail and forms. If the package renders a JS tab bar only on forms, guard detail usage or accept the fallback rendering.

Composing with native Nova layout. Don't nest NovaTabs inside Panel and then put Panel inside a tab — double chrome looks bad and some implementations break event handling. Pick one layout primitive per level: NovaTabs at the top, Panel/Collapsible inside tabs (if supported), never the reverse.

Insulating your resource from the package. Structure fields() so the tab container is the only package-coupled line:

return $this->tabbed([
    'General' => [...],
    'Content' => [...],
]);

protected function tabbed(array $groups): array
{
    return NovaTabs::make('Details')
        ->tabs($groups)   // adjust to actual API
        ->toArray();
}

Wrapping it in one helper means swapping to eminiarts/nova-tabs, kosatyi/nova-tabs, or native Nova field groups is a single-method change — and a 2.x → 3.x bump only touches that method.

Upgrade hygiene across the 2.x line. Since the package versions frequently at patch level (2.30 → 2.31), treat patch bumps as behavioural. Read the compare diff link in each release note (e.g. compare/v2.30.0...v2.31.0) before deploying; the 2.31.0 diff is deprecation-only, but that is not guaranteed for future patches.

Index columns are unaffected. Tabs are a form/detail concern; add sortable()/searchable() on the underlying fields as normal and keep fields()/fieldsForIndex separate if your resource splits them.

Gotchas and Tips

  • PHP 8.4: upgrade or silence. On 2.30.x and below you will get deprecation warnings on PHP 8.4 (implicit nullable params / similar). v2.31.0 fixes them. If your log channel is noisy or your test suite converts deprecations to failures, this is the release to take. Conversely, if you are on PHP 8.3, 2.31.0 buys you nothing behavioural — it is a safe but inert upgrade.
  • Do not pin to 1.0.0 — that advice is outdated. The package is on 2.x. A caret constraint (^2.31) is reasonable now that the package is receiving at least dependency-level maintenance. Still commit composer.lock, and if you need guaranteed API stability for production, vendor-fork into packages/ with a path repository.
  • Low-traffic still means low support. A merged PR is not a maintainer roadmap. There is effectively no community to answer "why is my tab empty" questions — read the source; assume nothing works until you see it in a browser.
  • Validation errors and hidden tabs. If a tab is hidden via canSee but a field inside it fails validation, Nova may report an error on a field the user cannot see. Keep required fields in the first tab, or ensure the failing tab is always visible.
  • dependsOn across tab boundaries. Field dependency resolution is DOM-based in Nova; a field in Tab B depending on a field in Tab A may fail because the controller isn't mounted when the tab is lazily rendered. Keep dependent pairs in the same tab.
  • Asset caching. After install or upgrade, always run php artisan nova:assets and hard-refresh. A stale public/vendor/nova-tabs directory manifests as "the field renders as a plain text input" — a classic Nova package symptom. This bites hardest right after a version bump.
  • Positional fragility. If the package inserts its tab bar via ->withMeta() and a JS mounted() hook, it is sensitive to field array order. Keep NovaTabs as the sole entry in fields(), not appended after other fields.
  • Inspect the payload during debugging. dump((new YourResource($model))->fields(NovaRequest::create('/'))) in a route or Tinker gives you the resolved field array — faster than guessing about withMeta keys.
  • Extension point if you fork: the tab container is almost always a Laravel\Nova\Fields\Field subclass with a small component override. Adding ->withMeta(['tabs' => $groups]) and a thin Vue component is a ~50-line patch if you need to reimplement rather than adopt.
  • Alternatives worth benchmarking first: Nova's native field grouping/panels (Nova 5), eminiarts/nova-tabs (the long-standing option with real dependents), and kosatyi/nova-tabs. Unless you specifically need this package's API shape, adopting one of those avoids the documentation gap entirely.
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.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings