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

Dash Arrange Laravel Package

shreejan/dash-arrange

shreejan/dash-arrange is a Laravel package for building rearrangeable dashboards. It lets users reorder cards, widgets, or list items via drag and drop and persist their preferred layout, making customizable dashboard arrangements simple to implement.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, single-maintainer package (4 stars, 0 dependents), so treat the source as the documentation. Budget 20–30 minutes to read src/, the published config, and the migration before wiring it in — it will be faster than guessing at conventions.

1. Install and publish

composer require shreejan/dash-arrange
php artisan vendor:publish --provider="Shreejan\DashArrange\DashArrangeServiceProvider"
php artisan migrate

Publish tags may not all exist; if --provider produces nothing, check the service provider for the exact tag names (config, migrations, assets) and publish individually.

2. Look here first, in this order

  • config/dash-arrange.php — the widget registry and grid/column settings. This is the contract everything else depends on.
  • The published migration — tells you the layout storage shape (almost certainly a user_id + JSON layout column) and therefore how you'll scope layouts.
  • The Blade component / view namespace — reveals the JS entry point and the DOM structure the drag-drop binds to.
  • routes/ or the provider's boot() — find the endpoint that persists a layout, and confirm what middleware it gets.

3. First use case: a personal dashboard

The canonical fit is "each user has a dashboard of cards they can reorder and hide." Register two or three widgets, drop the component into your dashboard view, and confirm the round-trip:

// config/dash-arrange.php
'widgets' => [
    'revenue'    => ['view' => 'dashboard.widgets.revenue',    'label' => 'Revenue'],
    'open_tasks' => ['view' => 'dashboard.widgets.open-tasks', 'label' => 'Open Tasks'],
    'activity'   => ['view' => 'dashboard.widgets.activity',   'label' => 'Recent Activity'],
],
{{-- resources/views/dashboard.blade.php --}}
<x-dash-arrange::board :widgets="config('dash-arrange.widgets')" />

Verify three things before building further: (a) reordering survives a page reload, (b) a brand-new user gets a sensible default rather than an empty board, (c) the save request is authenticated. If any of those fail, fix it in your own layer — don't wait on upstream.

Implementation Patterns

Wrap the package behind your own service, not scattered calls. With a 4-star, single-maintainer dependency, your biggest risk isn't bugs — it's abandonment. Keep DashArrange references in one place so swapping to spatie/laravel-dashboard, a hand-rolled JSON column, or gridstack.js is a contained refactor:

final class DashboardLayout
{
    public function forUser(User $user): array
    {
        return DashArrange::layoutFor($user)
            ?? $this->defaultLayout($user->role);
    }

    public function save(User $user, array $payload): void
    {
        DashArrange::saveLayout($user, $this->sanitize($payload));
    }
}

Always have a default-layout fallback. First-login users have no persisted row. Compute defaults by role or tenant rather than returning an empty board:

protected function defaultLayout(string $role): array
{
    return match ($role) {
        'admin'   => ['revenue', 'open_tasks', 'activity'],
        'billing' => ['revenue', 'invoices'],
        default   => ['activity'],
    };
}

Sanitize widget keys on save. Never persist whatever the client posts. Diff the submitted keys against array_keys(config('dash-arrange.widgets')) and drop unknowns — otherwise a stale browser tab silently writes a layout referencing widgets you've since renamed, and the board renders gaps.

Render each widget in isolation. Give every card its own Blade partial with its own data-fetching, and consider @each or a per-widget view composer so one slow widget (a costly aggregate query) doesn't delay the whole board. If the package supports lazy loading, use it for anything heavier than a trivial query.

Version your layout payloads. Store a layout_version alongside the JSON. When you add a widget to the config, you can then migrate persisted layouts (insert the new widget in a default position) instead of leaving existing users without it:

if (($layout['version'] ?? 1) < 2) {
    $layout['widgets']['invoices'] = ['visible' => true, 'order' => 99];
    $layout['version'] = 2;
}

Scope by tenant, not just user. If you're multi-tenant, add a tenant_id to the published migration before you run it in production. Retrofitting a scope onto an already-populated table is far more painful than editing the migration on day one, and this is exactly the kind of thing the package won't have anticipated.

Save debounced, and optimistically. Drag-drop fires many events. Debounce the persist call (~500ms) and update the DOM immediately; if the request fails, show a non-blocking toast rather than reverting the drag, which feels broken. Include a CSRF token — a fetch() without X-CSRF-TOKEN will 419 and the layout silently won't stick.

Test the round-trip, not the library. Feature-test your save endpoint with actingAs, an invalid widget key, and an oversized payload. Don't write tests against the package's internals — they'll break on any patch release.

Gotchas and Tips

  • Verify the real package name first. The repo is arragne-dashboard (typo) while the package is shreejan/dash-arrange. Confirm the exact Composer name with composer show shreejan/dash-arrange before writing docs for your team, and pin a version ("shreejan/dash-arrange": "^1.0") rather than using a floating constraint — with 0 dependents there's no ecosystem pressure to keep BC.
  • Assets are the most likely breakage. If the package ships pre-built CSS/JS, it may predate your Vite setup. Check for a resources/js source you can import into your own bundle instead of relying on a vendor:publish'd asset; if you must use the published assets, add a cache-busting query string, since published files often lack versioning.
  • Publish the migration, then read it. Don't run migrate blind. Confirm the cast (array vs json) matches how the package reads it back — a mismatch between your model cast and the package's internal decoding produces confusing "layout is empty" bugs.
  • Auth middleware on the save route is on you. Packages that auto-register routes routinely land them in the web group without auth. Check php artisan route:list | grep -i dash and either wrap the route in your own group or gate the controller.
  • Renaming a widget key orphans saved layouts. Keep keys stable and treat them as public API; change the label freely, never the key. If you must rename, write a data migration that rewrites the JSON for all users.
  • Concurrency. Two tabs open means last-write-wins on the whole layout blob. For most dashboards that's fine; if it matters, add an updated_at check and 409 on stale writes.
  • Stale JSON in a config/route cache. After editing config/dash-arrange.php, run php artisan config:clear — otherwise newly registered widgets appear to "not exist" in production while working locally.
  • Debugging a board that won't persist: check the network tab for a 419/403/422 first, then Log::info() the sanitized payload at the controller. Nine times out of ten it's CSRF or a widget-key filter silently dropping everything.
  • Extension points to prefer: the published config (widget registry, grid columns) and the Blade view namespace. Overriding the package's JS is a dead end — you'll lose it on the next composer update, and with one maintainer, updates will be infrequent and unannounced when they come.
  • Have an exit plan. The layout is almost certainly just JSON in a column. If the package goes stale, reading that column directly into your own component is a half-day of work — which is precisely why the wrapper service in the previous section is worth the small upfront cost.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib