PHPackages                             truefans/laravel-reactable - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. truefans/laravel-reactable

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

truefans/laravel-reactable
==========================

Reusable user likes, reactions full stack implementation.

2.1.0(7mo ago)00[3 PRs](https://github.com/VahanDrnoyan/laravel_reactable/pulls)MITPHPPHP ^8.4CI passing

Since Oct 5Pushed 5mo agoCompare

[ Source](https://github.com/VahanDrnoyan/laravel_reactable)[ Packagist](https://packagist.org/packages/truefans/laravel-reactable)[ Docs](https://github.com/vahandr/laravel-reactable)[ GitHub Sponsors](https://github.com/TrueFans)[ RSS](/packages/truefans-laravel-reactable/feed)WikiDiscussions main Synced today

READMEChangelog (10)Dependencies (14)Versions (22)Used By (0)

Laravel Reactable
=================

[](#laravel-reactable)

[![Latest Version on Packagist](https://camo.githubusercontent.com/546923c2ac0b6496c1c248bbb319f5faaf8b93b2206a4418eb68add43851444d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7472756566616e732f6c61726176656c2d726561637461626c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/truefans/laravel-reactable)[![GitHub Tests Action Status](https://camo.githubusercontent.com/760a4170a2ec3f57e1bf9965e24fe9fb9ffeb0975242e620db46ce571e96bb6a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f566168616e44726e6f79616e2f6c61726176656c2d726561637461626c652f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/VahanDrnoyan/laravel-reactable/actions?query=workflow%3Atests+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/e8ff820a11d97b8d715984716c5172d983d1244b656bc9be86c663551b67caa7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7472756566616e732f6c61726176656c2d726561637461626c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/truefans/laravel-reactable)

A beautiful, Facebook-style reactions and comments system for Laravel with Livewire. Add customizable emoji reactions and full-featured commenting to any model in your Laravel application with a single trait.

📸 Demo
------

[](#-demo)

### Reactions System

[](#reactions-system)

**Light Theme**[![Reaction Picker Light](docs/images/demo1.png)](docs/images/demo1.png)[![Reaction Picker Light](docs/images/demo4.png)](docs/images/demo4.png)

**Dark Theme**[![Reaction Picker Dark](docs/images/demo2.png)](docs/images/demo2.png)[![Reaction Picker Light](docs/images/demo3.png)](docs/images/demo3.png)

### Comments System

[](#comments-system)

**Light Theme**[![Comments Light](docs/images/demo5.png)](docs/images/demo5.png)

**Dark Theme**[![Comments Dark](docs/images/demo6.png)](docs/images/demo6.png)

✨ Features
----------

[](#-features)

### Reactions

[](#reactions)

- 🎭 **Facebook-Style UI** - Beautiful reaction picker with hover/click interactions
- 🔥 **Livewire Powered** - Real-time reactions without page refresh
- 📦 **Polymorphic Relations** - React to Posts, Comments, Images, or any model
- 🎨 **Fully Customizable** - Configure reaction types, icons, colors via config
- 👥 **User Reactions List** - See who reacted with filterable tabs by reaction type
- ♾️ **Infinite Scrolling** - Seamlessly load more reactions as you scroll

### Comments

[](#comments)

- 💬 **Full Commenting System** - Add, edit, and delete comments
- 😍 **Reactions on Comments** - Users can react to comments (configurable)
- ✏️ **Inline Editing** - Edit comments with validation and XSS protection
- 🗑️ **Custom Delete Modal** - Beautiful confirmation modal with Alpine.js
- 🔄 **Load More Pagination** - Efficient pagination for long comment threads
- 🛡️ **XSS Protection** - Built-in sanitization and validation

### General

[](#general)

- 🌙 **Dark Mode Support** - Beautiful UI in both light and dark themes
- ⚡ **Optimized Queries** - Eager loading prevents N+1 queries
- 📱 **Responsive Design** - Works perfectly on mobile and desktop
- ♿ **Accessibility First** - Full keyboard navigation, focus management, and ARIA attributes

---

📋 Requirements
--------------

[](#-requirements)

- PHP 8.4+
- Laravel 11.0+ or 12.0+
- Livewire 3.0+
- Alpine.js (included with Livewire)

---

🚀 Installation
--------------

[](#-installation)

### Step 1: Install via Composer

[](#step-1-install-via-composer)

```
composer require truefans/laravel-reactable
```

### Step 2: Publish Assets

[](#step-2-publish-assets)

```
php artisan vendor:publish --provider="TrueFans\LaravelReactable\LaravelReactableServiceProvider"
```

### Step 3: Run Migrations

[](#step-3-run-migrations)

```
php artisan migrate
```

---

📖 Usage
-------

[](#-usage)

### Reactions

[](#reactions-1)

**1. Add trait to your model:**

```
use TrueFans\LaravelReactable\Traits\HasReactions;

class Post extends Model
{
    use HasReactions;
}
```

**2. Eager load in controller (prevents N+1 queries):**

```
public function index()
{
    $posts = Post::with(['user', 'reactions'])
        ->latest()
        ->paginate(10);

    return view('posts.index', compact('posts'));
}
```

**3. Display in Blade:**

```

```

### Comments

[](#comments-1)

**1. Add trait to your model:**

```
use TrueFans\LaravelReactable\Traits\HasComments;

class Post extends Model
{
    use HasComments;
}
```

**2. Eager load comment counts (prevents N+1 queries):**

```
public function index()
{
    $posts = Post::with(['user', 'reactions'])
        ->withCount('comments')  // Important for performance!
        ->latest()
        ->paginate(10);

    return view('posts.index', compact('posts'));
}
```

**3. Display in Blade:**

```

```

### Configuration

[](#configuration)

Customize reactions and enable/disable features in `config/reactable.php`:

```
return [
    'reaction_types' => [
        'like' => ['icon' => '👍', 'label' => 'Like', 'color' => 'blue'],
        'love' => ['icon' => '❤️', 'label' => 'Love', 'color' => 'red'],
        // Add your own custom reactions!
    ],

    'comments' => [
        'enable_reactions' => true, // Allow reactions on comments
    ],
];
```

---

🎯 Advanced Usage
----------------

[](#-advanced-usage)

### Trait Methods

[](#trait-methods)

The `HasReactions` trait provides these methods:

```
// Add/remove reactions
$post->react('like', $user);  // User defaults to auth()->user()
$post->unreact($user);

// Check reactions
$post->hasReactedBy($user);  // Returns bool
$post->getReactionBy($user);  // Returns 'like', 'love', etc. or null

// Get counts
$post->getReactionsSummary();  // Returns ['like' => 5, 'love' => 3, ...]
$post->getTotalReactionsCount();  // Returns int
$post->getReactionsCountByType('like');  // Returns int
```

The `HasComments` trait provides these methods:

```
// Add/remove comments
$post->addComment('Great post!', $user);
$post->removeComment($commentId, $user);

// Check comments
$post->hasCommentedBy($user);  // Returns bool
$post->comments()->count();  // Get total comments
```

### Custom Reaction Permissions

[](#custom-reaction-permissions)

Override the `canReact` method in your model to control who can react:

```
class Post extends Model
{
    use HasReactions;

    public function canReact(string $type): bool
    {
        // Prevent users from reacting to their own posts
        if (auth()->id() === $this->user_id) {
            return false;
        }

        return true;
    }
}
```

---

🎯 Advanced Usage
----------------

[](#-advanced-usage-1)

**IMPORTANT:** Always eager load relationships when displaying multiple models with Reactions or Comments components.

#### For Reactions Component

[](#for-reactions-component)

```
// ✅ CORRECT - Eager load reactions
$posts = Post::with(['user', 'reactions'])
    ->latest()
    ->paginate(10);

// ❌ WRONG - Will cause N+1 queries
$posts = Post::with('user')
    ->latest()
    ->paginate(10);
```

#### For Comments Component

[](#for-comments-component)

```
// ✅ CORRECT - Eager load comment counts
$posts = Post::with(['user', 'reactions'])
    ->withCount('comments')  // Adds comments_count attribute
    ->latest()
    ->paginate(10);
```

#### Complete Example Controller

[](#complete-example-controller)

```
use App\Models\Post;
use Illuminate\View\View;

class PostController extends Controller
{
    public function index(): View
    {
        $posts = Post::with(['user', 'reactions'])
            ->withCount('comments')
            ->latest()
            ->paginate(10);

        return view('posts.index', compact('posts'));
    }

    public function show(Post $post): View
    {
        $post->load('user');
        $post->loadCount('comments');

        return view('posts.show', compact('post'));
    }
}
```

### Database Queries

[](#database-queries)

Get posts with specific reactions:

```
// Get posts with specific reaction
$lovedPosts = Post::whereHas('reactions', function($query) {
    $query->where('type', 'love');
})->get();

// Count reactions for a post
$reactionCount = $post->reactions()->count();

// Get all users who reacted to a post
$users = $post->reactions()->with('user')->get()->pluck('user');
```

---

🧪 Testing
---------

[](#-testing)

Create test data with the included seeder:

```
use TrueFans\LaravelReactable\Models\Reaction;

// Create reactions programmatically
public function run(): void
    {

        $reactionTypes = ['like', 'love', 'laugh', 'wow', 'sad', 'angry'];

        Post::factory()
            ->count(10)
            ->has(
                Reaction::factory()
                    ->count(100)
                    ->state(fn() => [
                        'user_id'        => User::factory(),
                        'type'           => fake()->randomElement($reactionTypes),
                        'reactable_type' => Post::factory(),
                    ])
            )
            ->create();

    }
```

---

♿ Accessibility Features
------------------------

[](#-accessibility-features)

### Keyboard Navigation

[](#keyboard-navigation)

- Full keyboard navigation support for all interactive elements
- **Tab** - Move between interactive elements
- **Enter/Space** - Activate buttons and toggles
- **Escape** - Close open dialogs and return focus to the triggering element
- **Arrow Keys** - Navigate between reaction options and filter tabs
- **Home/End** - Jump to first/last item in lists

### ARIA Attributes

[](#aria-attributes)

- `role="menu"` and `role="menuitem"` for reaction picker
- `role="dialog"` for modals with proper labeling
- `aria-expanded` to indicate expandable/collapsible elements
- `aria-pressed` for toggle buttons
- `aria-live` regions for dynamic content updates
- `aria-busy` for loading states
- `aria-label` and `aria-labelledby` for better screen reader context

### Focus Management

[](#focus-management)

- Focus is trapped within open dialogs
- Focus returns to the triggering element when dialogs close
- Focus is managed during dynamic content loading
- Hidden elements are removed from the tab order

### Screen Reader Support

[](#screen-reader-support)

- All interactive elements have appropriate labels
- Status messages for reactions and loading states
- Semantic HTML structure for better navigation
- Hidden text for screen readers where visual context is insufficient

### Color Contrast

[](#color-contrast)

- Meets WCAG 2.1 AA contrast requirements
- Proper color contrast in both light and dark modes
- Visual indicators beyond color for interactive states

🎨 UI Components &amp; Interactions
----------------------------------

[](#-ui-components--interactions)

### Main Like Button

[](#main-like-button)

- **Default State:** Shows thumbs-up icon with "Like" text
- **After Reacting:** Shows your reaction emoji and label (e.g., "❤️ Love")
- **Click:** Opens reaction picker (or removes your reaction if already reacted)
- **Hover Effect:** Background changes to indicate interactivity
- **Color:** Blue when you've reacted, gray when you haven't

### Reaction Picker

[](#reaction-picker)

- **Appears:** Above the Like button on hover
- **Layout:** All reaction emojis in a single horizontal row
- **Smart Positioning:** Uses Alpine.js Anchor plugin for intelligent placement
    - **Auto-flip:** Automatically repositions to stay within viewport
    - **Placement:** Prefers top-end (above button, right-aligned)
    - **Fallback:** Flips to bottom or sides if no space above
    - **Offset:** 8px gap from button for better spacing
- **Interactions:**
    - Hover over emoji → Scales up with animation
    - Click emoji → Saves reaction and closes picker
    - Hover away → Closes picker automatically
- **Animations:** Smooth fade-in/out with scale transitions
- **Accessibility:** ARIA labels and focus rings for keyboard navigation

### Reaction Count Summary

[](#reaction-count-summary)

- **Display:** Top 3 reaction icons as overlapping circles + total count
- **Position:** Left side of the component (opposite of Like button)
- **Clickable:** Click to open detailed reactions list
- **Hover Effect:** Background changes to indicate it's clickable
- **Dynamic:** Only appears when post has reactions

### Reactions List Dropdown

[](#reactions-list-dropdown)

- **Opens:** When clicking on reaction count summary
- **Smart Positioning:** Uses Alpine.js Anchor plugin for intelligent placement
    - **Auto-flip:** Automatically repositions to stay within viewport
    - **Placement:** Prefers top-start (above button, left-aligned)
    - **Fallback:** Flips to bottom or sides if no space above
    - **Offset:** 8px gap from button for better spacing
- **Width:** Auto-width (min 320px, max 448px) - expands based on content
- **Features:**
    - **Filterable Tabs:** Switch between "All" and specific reaction types
    - **Active Tab:** Highlighted in blue
    - **Tab Counts:** Shows number of each reaction type
    - **Tab Wrapping:** Tabs wrap to multiple lines if needed (no horizontal scroll)
    - **User List:**
        - User avatar (first letter in gradient circle)
        - User name
        - Reaction time (e.g., "2 hours ago")
        - Reaction emoji on the right
    - **No Scrollbars:** Full height display without vertical scrolling
    - **Click Outside:** Closes dropdown automatically
    - **Hover Effects:** Each user row highlights on hover
- **Accessibility:** Proper ARIA attributes and keyboard navigation

### Smart Positioning Technology

[](#smart-positioning-technology)

- **Powered by:** Alpine.js Anchor plugin (included with Livewire by default)
- **Benefits:**
    - Zero configuration required
    - Automatic viewport detection
    - Intelligent fallback positioning
    - No overflow or clipping issues
    - Works with scrolling and resizing
    - Lightweight (~2KB)
- **How it works:**
    - Monitors button position in real-time
    - Calculates available space in all directions
    - Automatically chooses best position
    - Smoothly transitions between positions

### Responsive Design

[](#responsive-design)

- Works perfectly on mobile and desktop
- Touch-friendly button sizes
- Prevents horizontal scrolling
- Proper z-index layering for dropdowns
- Smart positioning adapts to screen size
- No fixed positioning issues on mobile

---

🧪 Testing
---------

[](#-testing-1)

The package includes a comprehensive test suite with 59 tests covering all functionality.

```
# Run tests
composer test

# Run tests with coverage
composer test-coverage

# Run specific test file
vendor/bin/pest tests/HasReactionsTraitTest.php
```

**Test Coverage:**

- ✅ HasReactions trait (20 tests)
- ✅ Reaction model (11 tests)
- ✅ Livewire component (26 tests)
- ✅ Architecture tests (2 tests)

For detailed testing documentation, see [tests/README.md](tests/README.md) and [tests/TESTING\_GUIDE.md](tests/TESTING_GUIDE.md).

---

🤝 Contributing
--------------

[](#-contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

**Development Setup:**

1. Clone the repository
2. Run `composer install`
3. Run tests with `composer test`
4. Make your changes
5. Ensure all tests pass
6. Submit a PR

---

📝 License
---------

[](#-license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

---

👨‍💻 Credits
-----------

[](#‍-credits)

- [Vahan Drnoyan](https://github.com/VahanDrnoyan)
- [All Contributors](../../contributors)

---

🆘 Support
---------

[](#-support)

If you discover any issues or have questions, please [open an issue](https://github.com/truefans/laravel-reactable/issues).

---

**Made with ❤️ by TrueFans**

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance69

Regular maintenance activity

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 96% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

11

Last Release

224d ago

Major Versions

1.1.9 → 2.0.02025-11-08

### Community

Maintainers

![](https://www.gravatar.com/avatar/3930a3616e04af3787c975859c0df28830bedfb80796c16ea3c7c5906c29dcf0?d=identicon)[VahanDr.](/maintainers/VahanDr.)

---

Top Contributors

[![VahanDrnoyan](https://avatars.githubusercontent.com/u/23120503?v=4)](https://github.com/VahanDrnoyan "VahanDrnoyan (72 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (3 commits)")

---

Tags

laravellaravel-reactableTrueFans

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/truefans-laravel-reactable/health.svg)

```
[![Health](https://phpackages.com/badges/truefans-laravel-reactable/health.svg)](https://phpackages.com/packages/truefans-laravel-reactable)
```

###  Alternatives

[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M47](/packages/spatie-laravel-pdf)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

329530.5k29](/packages/codewithdennis-filament-select-tree)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.6k](/packages/rawilk-profile-filament-plugin)[worksome/exchange

Check Exchange Rates for any currency in Laravel.

124603.0k](/packages/worksome-exchange)[tarfin-labs/event-machine

Event-driven state machines for Laravel with event sourcing, type-safe context, and full audit trail.

199.4k](/packages/tarfin-labs-event-machine)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
