harvirsidhu/filament-action-overflow

Compose any Filament action list into primary actions plus an overflow dropdown, with divider support.

Maintainers

Package info

github.com/harvirsidhu/filament-action-overflow

pkg:composer/harvirsidhu/filament-action-overflow

Statistics

Installs: 357

Dependents: 0

Suggesters: 0

Stars: 3

Open Issues: 0

v1.1.0 2026-05-05 07:10 UTC

This package is auto-updated.

Last update: 2026-05-05 07:11:42 UTC


README

Latest Version on Packagist Total Downloads

Turn any Filament action list into a few primary buttons + a "More" dropdown, automatically.

[ Edit ] [ Archive ] [ ⋮ More ▾ ]
                       ├─ Publish
                       ├─ Delete
                       └─ Download

You write a flat list of actions; the package decides which sit out front and which get tucked under More. Works anywhere Filament accepts an action array — page headers, table actions, record actions, bulk actions, widgets.

Why use it

  • One line. Append ->withOverflow() to an ActionGroup and you're done.
  • Smart defaults. No overflow → no More. One overflow action → flattened. Two or more → grouped.
  • Divider aware. Filament 5's ->dropdown(false) divider sections pass through into the More menu cleanly.
  • Hidden-action aware. ->hidden() / ->visible(false) / (optionally) ->authorize(...) are honored before composing.

Compatibility

Package Versions
Filament ^4.0^5.0
PHP ^8.2

Installation

composer require harvirsidhu/filament-action-overflow

The package works without any config. Publish only if you want to change defaults:

php artisan vendor:publish --tag="filament-action-overflow-config"

Quick start

The macro is the simplest path. Build an ActionGroup like normal, append ->withOverflow($primary), and return the result.

use Filament\Actions\Action;
use Filament\Actions\ActionGroup;

public function getHeaderActions(): array
{
    return ActionGroup::make([
        Action::make('edit'),
        Action::make('archive'),
        Action::make('delete'),
    ])->withOverflow(1);
    // → [ Edit ] [ ⋮ More ▾ (Archive, Delete) ]
}

->withOverflow() is terminal — it returns the composed array<Action | ActionGroup> ready for getHeaderActions(), getTableActions(), getRecordActions(), etc.

By default both the primary actions and the More trigger are promoted to button view, so you get a row of matching buttons without sprinkling ->button() on each action.

Customizing the More button

For control over the dropdown's label, icon, color, etc., use the ActionOverflow facade:

use Filament\Actions\Action;
use Harvirsidhu\FilamentActionOverflow\Facades\ActionOverflow;

return ActionOverflow::make([
    Action::make('edit'),
    Action::make('archive'),
    Action::make('delete'),
])
    ->primaryCount(2)
    ->label('Options')
    ->icon('heroicon-m-bars-3')
    ->color('gray')
    ->toActions();

Dividers (sections inside the More menu)

A dropdown(false) group nested inside another ActionGroup is Filament's way of saying "render these as a separated section." This package treats them as first-class:

use Filament\Actions\Action;
use Filament\Actions\ActionGroup;

return ActionGroup::make([
    Action::make('submit'),

    ActionGroup::make([
        Action::make('discount'),
        Action::make('tax'),
        Action::make('rounding'),
    ])->dropdown(false),

    ActionGroup::make([
        Action::make('change-billing'),
        Action::make('refresh'),
    ])->dropdown(false),
])->withOverflow(1);

Renders as:

[ Submit ] [ ⋮ More ▾ ]
              ├─ ──────────
              ├─ Discount
              ├─ Tax
              ├─ Rounding
              ├─ ──────────
              ├─ Change billing
              └─ Refresh

How dividers behave in the composer:

  • A divider doesn't take up a primary slot — its children do. With primaryCount: 2 and a divider whose first child is submit, submit is promoted to primary.
  • Dividers never appear among side-by-side primary buttons (they only make sense inside a dropdown).
  • A divider at the very top of the More menu is unwrapped (no orphan line above the first item).
  • Trailing and adjacent dividers stay as distinct sections — exactly how Filament renders them natively.
  • Hidden / invisible children are dropped from dividers; if every child is dropped, the divider disappears.

Hidden, invisible, unauthorized

Hidden and invisible actions are filtered automatically:

return ActionOverflow::make([
    Action::make('edit')->hidden(),         // dropped
    Action::make('archive'),                // kept
    Action::make('delete')->visible(false), // dropped
    Action::make('publish'),                // kept
])->toActions();

Authorization filtering is opt-in because Filament's renderer already disables unauthorized actions visually:

return ActionOverflow::make($actions)
    ->filterUnauthorized()
    ->toActions();

Reference

Config keys (config/action-overflow.php)

Key Type Default What it does
primary_count int 1 How many primary actions to surface
label string 'More' The dropdown trigger's label
icon string|enum 'heroicon-m-ellipsis-vertical' Trigger icon
color string 'gray' Filament color name for the trigger
hidden_label bool false Hide the trigger label, show icon only
button bool true Promote primary + trigger to button view
icon_position string|enum 'after' 'before' or 'after' (or IconPosition)
filter_unauthorized bool false Drop unauthorized actions before composing

icon accepts a string, a BackedEnum (e.g. Filament\Support\Icons\Heroicon::EllipsisVertical on Filament 5), or null for the default. icon_position accepts the string forms or a Filament\Support\Enums\IconPosition enum. Strings are the published defaults so the file loads cleanly on both Filament 4 and 5.

Fluent API

ActionOverflow::make($actions)
    ->primaryCount(int $count = 1)
    ->label(string $label = 'More')
    ->icon(string|\BackedEnum|null $icon = null)
    ->color(string $color = 'gray')
    ->hiddenLabel(bool $state = true)
    ->button(bool $state = true)
    ->iconPosition(\Filament\Support\Enums\IconPosition|string|\BackedEnum|null $position = \Filament\Support\Enums\IconPosition::After)
    ->filterUnauthorized(bool $state = true)
    ->toActions();

->button(false) is opt-out only — it stops the composer from calling ->button(). If a caller already passed a pre-buttoned action, it keeps its button view.

Testing

composer test

Changelog

See CHANGELOG.

Credits

License

MIT — see LICENSE.