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

Filament Relation Manager Component Laravel Package

njxqlus/filament-relation-manager-component

Use Filament Relation Managers anywhere in your app, not just on resource pages. Drop them into forms, schema components, or tabs with a simple RelationManager component, control lazy loading, and optionally publish the package views.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require njxqlus/filament-relation-manager-component

# only if you intend to override the blade markup
php artisan vendor:publish --tag="filament-relation-manager-component-views"

Choose the right major version first. This is the single most important setup decision, because the two branches target different Filament majors:

Package Filament Laravel
^1.1 v3 up to 11/12 (see 1.1.3)
^2.2 v4 / v5 up to 13 (2.2.2)

Check composer.json constraints on the tag you install rather than trusting the README tables — the 1.1.3 and 2.2.2 changelogs both advertise "Laravel 13 compatibility", so version-to-framework mapping is ambiguous from release notes alone.

Where to look first

  1. README.md → the two usage blocks (form + infolist).
  2. src/Components/Forms/RelationManager.php and src/Components/Infolists/RelationManager.php — the whole public API is make(), manager(class-string), lazy(bool), plus everything inherited from Schema\Component (visible(), columnSpanFull(), etc.).
  3. resources/views/ after publishing — this is the only real customization surface.

First use case: an Edit page where related records currently live in a separate Relation Manager tab that is hard to reach. Move it inline:

use Njxqlus\Filament\Components\Forms\RelationManager;

public static function form(Schema $schema): Schema
{
    return $schema->schema([
        Tabs::make()->tabs([
            Tabs\Tab::make('Versions')->schema([
                RelationManager::make()
                    ->manager(RelationManagers\VersionsRelationManager::class)
                    ->lazy(false),
            ]),
        ]),
    ]);
}

The manager class is used unmodified — no traits, no base class, no attribute. If it already works on the resource page, it works here.


Implementation Patterns

Tabs-as-navigation for relation-heavy resources

The canonical pattern: one tab per relation, lazy(false) on the tab you want warm, lazy(true) (default) on the rest.

Tabs::make()->persistTabInQueryString()->tabs([
    Tabs\Tab::make('Stands')
        ->schema([RelationManager::make()->manager(StandsRelationManager::class)->lazy(false)]),
    Tabs\Tab::make('Contexts')
        ->schema([RelationManager::make()->manager(ContextsRelationManager::class)->lazy()]),
]);

Use lazy(false) only for the tab the user lands on. Everything else deferred — a resource with four embedded tables otherwise fires four paginated queries plus four header/summary queries on every page load.

Form vs. Infolist variant — they are distinct classes

use Njxqlus\Filament\Components\Forms\RelationManager as FormRelationManager;
use Njxqlus\Filament\Components\Infolists\RelationManager as InfolistRelationManager;

The Infolist variant is the one you want on ViewRecord pages: it renders the table read-mostly and is the correct choice when the surrounding schema is an Infolists\Infolist. The 1.1.1 release was specifically a fix for "displaying relation manager in infolist", so infolist embedding is the younger code path — smoke-test it on your Filament minor.

Alias your imports

RelationManager collides with Filament\Resources\RelationManagers\RelationManager (the base class your managers extend) and with the resource's own namespace. Aliasing (as FormRelationManager) is not cosmetic here — it prevents accidentally passing the component class where a manager class-string is expected.

Reuse across Edit and View

Since the component takes a plain class-string, you can drive it from a static map on the resource and keep the form/infolist definitions in sync:

protected static function embeddedManagers(): array
{
    return [
        'Versions' => RelationManagers\VersionsRelationManager::class,
        'Stands'   => RelationManagers\StandsRelationManager::class,
    ];
}

Build the Tab::make($label) loop from that map in both form() and infolist().

Prefer the component over a bespoke custom page

Before this package, inline editing of relations meant either a custom Filament page with a hand-rolled table, or a RelationManager exposed via getRelations() on a page you had to build. The component removes that: getRelations() stays empty, and the table lives where the user already is. Migrate those custom pages when you touch them; don't rewrite them proactively.

Authorization

The embedded manager inherits the manager's own canViewForRecord() / can* policies, evaluated against the owner record resolved by the component. That means embedding does not bypass policy checks — but it also means a manager whose canViewForRecord() you tightened for the resource page behaves identically in a form. Verify with a user who lacks the relation permission before shipping, and layer ->visible() on the component if you want the tab to disappear rather than render an empty table.


Gotchas and Tips

The owner record must exist. This is the failure mode you will hit first. Embedding in form() on a Create page means there is no $record, so the manager's getOwnerRecord() cannot be satisfied. Restrict embedding to EditRecord, ViewRecord, and infolist contexts — or guard with ->visible(fn ($record) => $record !== null). Symptom is usually a typed-property "must not be accessed before initialization" error or an empty table, not a friendly exception.

Pin ^2.1 or later if you use modals/slideovers. 2.1.0 is literally "Fix ownerRecord assignment to work with modal/slideover". On 2.0.x and below, embedding inside a modal form leaves the owner record unset. If a tabbed relation manager works on a full page but dies in a ->modal() action form, this is why.

Lazy is on by default. The README passes ->lazy(false) on every example, which reads as boilerplate but is required — the component defaults to lazy. A lazily embedded table inside a tab that Filament never activates will simply never render, and inside a modal the placeholder may not resolve at all. When in doubt, lazy(false) first, optimize later.

Don't double-register. If the same manager class appears in getRelations() on the page and as an embedded component, you get two Livewire-mounted tables, duplicate action handlers, and confusing notification attribution. Pick one location per manager.

README namespaces are inconsistent. The infolist example mixes Schemas\Components\Tabs (outer) with Schemas\Infolists\Components\Tabs\Tab (inner tab). On Filament v3 the namespaces are entirely different (Filament\Forms\Components\Tabs) and Schemas\* does not exist. Copy the structure, then import from your installed version's actual class list — php artisan filament:about or IDE autocomplete, not the README.

Nested tables are expensive. Each embedded manager is a full table: query, pagination, filters, header actions, bulk actions. Three lazily-loaded tabs are fine; three non-lazy tabs plus a large form on a slow connection produce a visible stall, because everything hydrates in one Livewire request. Profile with the query log before blaming the package.

Publishable views are the extension point — and the trap. php artisan vendor:publish --tag="filament-relation-manager-component-views" copies the markup into your app, giving you full control over wrapper, spacing, and the loading placeholder. But published views are snapshot copies: a future package release will not update them, and if the package changes its view variables your copy silently breaks. Publish only when you actually need to change markup, and diff against vendor/njxqlus/.../resources/views after every upgrade.

Small-package risk. 46 stars, 0 dependents on Packagist, and a release history dominated by dependency bumps and Filament-major catch-up PRs. It is a thin wrapper, not a framework — but pin it in composer.json, read the diff of every minor bump, and keep the migration path in mind: the underlying capability is a handful of lines of view rendering around RelationManager::make(), so if the package stalls you can vendor the two component classes into app/Filament/Components/ and drop the dependency without touching a single manager class. That reversibility is the strongest argument for adopting it.

Debugging checklist when a table renders blank:

  1. Is the surrounding tab activated? (lazy + inactive tab = nothing)
  2. Does the record exist? (Create page = no owner record)
  3. Is canViewForRecord() returning true for this user?
  4. Is the manager also in getRelations()? (double mount)
  5. ->lazy(false) applied as the last call on the component, not before ->manager() — chaining order is fine for these two methods, but mixing them into a conditional ->when() chain is where it silently drops.

Testing. Because the manager is unmodified, existing Relation Manager tests keep passing — the embedded component is a rendering concern only. Cover it with a Livewire test that assertSees a known related record on the Edit page, and one negative test on a Create page asserting the tab is absent. That pair catches the owner-record and double-registration regressions, which are the only two failure modes this package realistically introduces.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core