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

Laravel Activitylog Ui Laravel Package

nsd7/laravel-activitylog-ui

nsd7/laravel-activitylog-ui adds a clean, ready-to-use web interface for viewing Laravel activity logs. Browse, search, filter, and inspect logged events and changes without building your own admin screens. Ideal for Spatie laravel-activitylog users.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Confirm you actually have activity data first. This package is a read-only presentation layer over spatie/laravel-activitylog's activity_log table. If LogsActivity is not implemented on the models you care about, the dashboard renders an empty shell and you have silently started an instrumentation project instead. Before installing, sanity-check:

php artisan tinker
>>> Spatie\Activitylog\Models\Activity::count();

If that returns zero, stop and instrument first. If it returns data, note the spread of log_name, event, subject_type, and causer_type values — that determines whether the filters will be useful on day one.

2. Install and let auto-discovery do its job.

composer require nsd7/laravel-activitylog-ui

The service provider, route group, and precompiled Tailwind assets are registered automatically. There is no npm install and no build step — this is the package's main architectural selling point and the reason it drops cleanly into Forge/shared-hosting/lean-container deploys.

3. Publish and read the config before touching the browser. Use the publish tags listed in the configuration guide (tags are the authoritative reference; don't guess them). The config file is where you answer the questions that actually matter operationally: route prefix, route middleware, authorization gate, default pagination, and database connection. Get these right before you first load the dashboard, because the default state is the thing you should be verifying, not trusting.

4. Visit the dashboard, then immediately harden it. Load the configured route as a normal authenticated user who is not an admin and confirm you get a 403/404. If you get content, the route is live without a gate and you own that fix before anything else.

5. First real use case — the support ticket. Open a record you know changed, filter to its subject_type/subject_id, and read the before/after diff. That single workflow ("why did this record change, and who did it?") is your fastest demonstration of value. Time-to-answer on that question before and after install is the metric worth tracking.

Where to look first, in order: config file → authorization guide → dashboard filters → the JSON API guide. The API is a second entry point to the same sensitive data and needs its own decision, not a footnote.

Implementation Patterns

Mount it behind your existing admin boundary, not beside it. Put the package's route group inside the middleware stack your admin area already uses (web, auth, plus whatever role/permission middleware you run), and additionally register a dedicated gate — something like viewAuditLog — rather than relying on is_admin checks scattered elsewhere. A named ability is what lets you later grant auditors read-only access without inventing a second admin tier.

// AppServiceProvider::boot()
Gate::define('viewAuditLog', fn (User $u) => $u->hasRole('admin') || $u->hasRole('auditor'));

Then reference that gate from the package's route middleware config. Treat it as required configuration, not optional hardening.

Instrumentation determines UI quality — invest in it deliberately. The dashboard can only render what Spatie records. Patterns that pay off:

  • Use logOnly([...]) or an explicit attribute list rather than logAll() on models with noisy or secret columns. Every field you log becomes a field visible in a diff, an export, and the JSON API.
  • Use logName() (or useLogName) to partition logs by domain — billing, auth, content. These become the cleanest filter dimension in the UI and make saved views far more useful than filtering by class name.
  • Log meaningful event values (created, updated, deleted, plus custom ones like approved, refunded). The event filter is one of the highest-leverage controls.

Make causers and subjects human-readable. Model-aware views resolve causer/subject into labels — often via name or email. If your User model exposes only full_name, verify the viewer resolves it; if not, add an accessor rather than patching the views. Do the same for subjects: a bare App\Models\Invoice#4417 is far less useful than INV-4417 — Acme Ltd.

Deep-link from your own record pages. The highest-value integration is not the dashboard itself — it is a "History" link on each model's detail page that jumps into the viewer pre-filtered to that subject. That converts a separate tool into part of the workflow people already have open. Combine with a saved view per entity type so the filter set is one click.

Saved views for recurring investigations. Define a small, opinionated set — "Payment changes (last 24h)", "Role/permission changes", "Deleted records this week" — and treat them as team documentation. The value is that a support engineer does not have to reconstruct a filter query at 2am.

Timeline for incident forensics. The timeline is the right tool when you have a when but not a what: pin a window around an incident and a causer, then read the sequence. Analytics charts answer "how much activity, from whom, over time"; the timeline answers "what happened, in order".

Exports: decide the policy before enabling them. CSV for spreadsheet reconciliation, JSON for programmatic handoff, Excel/PDF for auditor deliverables. Two rules: (a) confirm the export dependencies (dompdf, maatwebsite/excel) are suggest in composer.json and only install what you actually need — you do not want a PDF engine pulled into production as a side effect of a UI package; (b) define retention and PII scope for exports before the first export leaves the building, because an export is a copy that leaves your access controls behind.

Multi-database support is the differentiator for sharded/split-tenant estates. If you run per-tenant connections, configure the connection per request (or per mounting) rather than assuming a single global connection. A single-connection viewer is a hard blocker in those setups, so this is the feature that decides adopt-vs-skip for that architecture.

Localization is documented — publish the translation files and add locales if your internal audience is non-English. Do this at rollout, not later; retrofitting translated audit vocabulary into saved views and exported headers is annoying.

Customization path. The views are publishable Blade + Tailwind. For light branding (headings, logo, colors), publish and edit. If you find yourself rewriting more than a couple of views, stop and wrap instead: keep the package's views intact and override at the layout/routing layer, so upgrading stays cheap. Patching vendored views deeply is how a two-day integration becomes a permanent fork.

Test the two things that break silently. A feature test asserting 403 for a non-authorized user on both the dashboard and the JSON API, and a test asserting that a logged value containing HTML renders escaped rather than interpreted.

Gotchas and Tips

Authorization is documented, not necessarily shipped. The authorization guide tells you how to gate; that is not the same as a deny-by-default route. Assume you own the gate regardless of what the docs imply, verify it on a fresh install, and re-verify after upgrades. This is the single highest functional risk in the package.

The JSON API is a second boundary. It exposes the same diff payloads over a different path. It may not inherit the Blade-oriented middleware assumptions you configured for the dashboard. Gate it explicitly, confirm it responds identically to your gate, and add it to your threat model — a reviewer will ask about it.

Diffs contain raw before/after values, including PII. Blade escaping should handle rendering, but escaping does not solve exposure. The correct fix is upstream: don't log the columns you don't want to display (logOnly, skip-attributes, or attribute-level redaction at the model). Redacting in the UI is a losing game because the API and exports bypass your Blade views. Decide what's sensitive before you turn on a viewer that makes it browsable.

Export dependencies: read composer.json, don't infer from docs. The docs describing CSV/JSON/Excel/PDF tells you nothing about whether dompdf or maatwebsite/excel are hard requirements. Confirm they're suggest; if they're require, you've just added a rendering stack to production for a feature you may not use.

Empty or confusing dashboard = data problem, not a bug. Before filing anything, check log_name/event value spread in the table. Filters that look broken are usually a mismatch between what's in the table and what the filter assumes.

Index the table. Pagination defaults protect the page, not the query. On anything beyond trivial volume, add indexes covering (subject_type, subject_id), causer_type/causer_id, created_at, log_name, and event. This is where the "hundreds of thousands to low millions of rows" claim either holds or falls over.

Docs track main, not your tag. The VitePress site auto-deploys from main, so published guidance can describe behavior you don't have. Pin your reading to the release tag you installed — and pin the dependency itself in composer.json rather than floating on ^. This matters more than usual given a single maintainer and no dependents.

Tailwind collisions are real. Precompiled assets may conflict with host styles (base resets, utility specificity, dark-mode class strategy). If the dashboard looks subtly wrong, scope the package's CSS to its layout container rather than escalating specificity across your app. Check dark-mode behavior explicitly if your app has its own theme toggle.

Route collisions and caching. An /admin prefix, a catch-all route, or a stale route:cache can all make the dashboard 404 or shadow an existing route. After configuring the prefix, run php artisan route:clear && php artisan config:clear before debugging anything else.

Treat it as an internal tool with an exit ramp. Bus factor 1, 16 stars, 0 dependents. Practical mitigations: pin the version, put the mounting behind a config flag or feature toggle so you can disable the route group and remove the provider in one deploy, and keep the code surface small enough that a fork is a contained decision. The data model is Spatie's — the UI is disposable, and that should stay true in your architecture.

Do not present it as tamper-evident. It is a read/browse UI over application-level rows, not an immutable ledger. If audit readiness is the driver, say "audit viewer" to stakeholders and auditors, and keep the WORM/immutability question with your actual security logging strategy.

Verify release metadata against Packagist/GitHub before pinning. The advertised release date on this package post-dates the current period — confirm the tag, the commit, and the composer.json constraints directly rather than relying on the listing. It costs two minutes and prevents pinning to something that doesn't resolve.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin