Download the PHP package paulohps/laravel-timeline without Composer
On this page you can find all versions of the php package paulohps/laravel-timeline. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package laravel-timeline
Laravel Timeline
A causer-agnostic activity timeline for Laravel. It gives you:
- A polymorphic
causerinstead of a hardcodeduser_id, so any model — aUser, aTeam, anOrder— can own a timeline. - A base
TimelineEventclass: your app defines the concrete events (what they're called, how they look, what they link to); the package handles storage, context capture and rendering. - A Livewire component (
<livewire:timeline />) with per-type filter chips, day grouping and pagination. - Framework-agnostic styling: every element carries a stable
lt-*CSS class. Use the shipped stylesheet (all CSS custom properties), override its variables, or style everything from scratch — no Tailwind/Bootstrap required.
Requires PHP 8.3+, Laravel 12+/13+ and Livewire 3.6+/4.
Installation
Publish and run the migration:
This creates the timeline_entries table:
| Column | Purpose |
|---|---|
causer_type / causer_id |
The model the timeline belongs to (morph). |
type |
The event key (e.g. login, order_shipped). |
subject_type / subject_id |
Optional morph to the model the event is about. |
metadata |
JSON: request context + anything you pass when recording. |
To rename the table, publish the config first and change timeline.table before migrating.
Defining events
The package ships only the abstract base class — your application defines the actual events. Generate one:
This creates app/Timeline/OrderShipped.php and reminds you to register it. The generated stub is minimal because the single required method is label():
Everything else is an optional override:
Registering events
Stored entries hold the event key, so the package needs a key → class map. Register your events in config/timeline.php:
…or at runtime (e.g. in a service provider), with the facade:
Entries whose type is no longer registered don't break the page: they render through a fallback that humanizes the raw key (legacy_thing → "Legacy Thing").
Recording entries
Add the HasTimeline trait to any model that should own a timeline:
Then record from wherever fits your app (listeners, jobs, controllers):
The trait also gives you the relation, newest first:
Because causer is a morph, the same works on any model — $team->recordTimelineEvent(...), $order->recordTimelineEvent(...) — with no schema changes.
Metadata & request context
Every entry stores a metadata JSON column. By default the current request's ip, user_agent and url are captured automatically; anything you pass when recording is merged on top:
Storing a title snapshot keeps entries meaningful even if the subject is later renamed or deleted.
To capture richer context (e.g. parsed browser/OS via a package like matomo/device-detector), replace the resolver:
The default detail() line renders browser · os · ip from whatever of those keys exist, so a resolver that adds browser/os upgrades the display automatically.
Enabling/disabling events
Every event has an on/off switch, read from config on each write — wire it to your own settings UI if you need runtime control:
Disabled events make record() return null instead of an entry. For different logic (per-tenant flags, feature flags), override enabled() on the event class.
Displaying the timeline
Drop the Livewire component anywhere, passing the model whose timeline should show:
Props:
| Prop | Default | Purpose |
|---|---|---|
causer |
— (required) | The model whose entries are listed. |
per-page |
config('timeline.per_page') (25) |
Page size. |
filterable |
true |
Show the per-type filter chips. |
only |
[] |
Restrict to a subset of event keys (also narrows the chips). |
The active filters sync to the URL (?event[]=login), so filtered views are shareable. Invalid keys in the URL are ignored.
Rendering entries yourself
The Livewire component is optional. Each entry renders through its event handler, so you can build your own listing:
Or skip the shipped item view entirely and use the handler's data:
Querying
Visual customization
The markup is intentionally plain HTML with stable, prefixed CSS classes. Three levers, from lightest to heaviest:
1. Use the shipped stylesheet and override variables
Everything funnels through CSS custom properties on .lt-timeline, so most branding is a few variable overrides in your own CSS:
Available variables: --lt-text, --lt-text-muted, --lt-surface, --lt-surface-hover, --lt-line, --lt-accent, --lt-accent-contrast, --lt-marker-{default|primary|success|info|warning|danger}-{bg|fg}, --lt-marker-size, --lt-font-size, --lt-font-size-sm.
2. Style the classes yourself
Skip the stylesheet and target the classes directly (they're part of the package's public API):
| Class | Element |
|---|---|
lt-timeline |
Component root. |
lt-filters / lt-filter / lt-filter--active |
Filter chip row / chip / selected chip. |
lt-day / lt-day__label / lt-day__line / lt-day__count |
Day separator: row, label ("Today"), rule, entry count. |
lt-entries / lt-entry / lt-entry__connector |
Entry list / one entry wrapper / vertical line between markers. |
lt-item |
One rendered row (also when you render entries manually). |
lt-item__marker / lt-item__marker--{color} |
Round marker / its color variant. |
lt-item__body / lt-item__header / lt-item__title / lt-item__meta |
Content column and title row. |
lt-item__time / lt-item__detail |
Timestamp / secondary line. |
lt-item__nav / lt-item__nav-toggle / lt-item__nav-menu / lt-item__nav-link |
Per-entry <details> dropdown. |
lt-empty |
Empty state. |
lt-footer / lt-footer__info |
Footer row / "Showing x–y of z". |
lt-pagination / lt-pagination__button |
Pager / prev-next buttons. |
Custom color() tokens work the same way: return 'purple' from an event and style .lt-item__marker--purple.
3. Publish the views
For structural changes (different markup, Tailwind classes, your design system's components):
resources/views/vendor/timeline/livewire/timeline.blade.php— the component: chips, day groups, pagination.resources/views/vendor/timeline/item.blade.php— a single row; receives$entry,$icon,$color,$title,$detail,$time,$navigation.
A single event can also bypass the shared item view by overriding render():
Translations & formats
Ships en and pt_BR strings ("Today", "No activity yet", …). Date/time display comes from config:
Artisan commands
make:timeline-event
To change the generated code, publish the stub — a stubs/timeline-event.stub in your app root takes precedence:
timeline:prune
Timelines grow forever by default. Prune old entries ad hoc or on a schedule:
Without --days, the cutoff comes from config('timeline.prune_days'); if both are missing the command fails instead of guessing.
Configuration reference
| Key | Default | Purpose |
|---|---|---|
table |
timeline_entries |
Storage table (set before migrating). |
model |
TimelineEntry::class |
Swap in your own entry model subclass. |
events |
[] |
Event classes registered on boot. |
enabled |
[] |
Per-key kill switches ('login' => false). |
per_page |
25 |
Livewire component page size. |
prune_days |
null |
Default cutoff for timeline:prune (null = require --days). |
date_format / time_format |
F j / H:i |
Display formats. |
Extending the entry model
The trait relation, the recorder and the Livewire component all resolve the model from config.
Testing your integration
A model factory ships with the package:
Package development
License
MIT.
All versions of laravel-timeline with dependencies
illuminate/contracts Version ^12.0|^13.0
illuminate/database Version ^12.0|^13.0
illuminate/support Version ^12.0|^13.0
livewire/livewire Version ^3.6|^4.0