Fermin Perdomo

Senior Full Stack Engineer | PHP | JavaScript

How to Add Reactions to Laravel Using reactify

Fermin Perdomo
July 19, 2025

I added a reaction system to my blog posts, and it doesn't have to be complicated. With Reactify, you can easily make any Eloquent model capable of receiving reactions. 


In this guide, I'll walk you through the step-by-step process of setting up Reactify. Let's get started!


Step 1: Install the Package

Start by installing the package via Composer:

composer require phpdominicana/reactify

Step 2: Publish the Config and Migrations

Publish the config and migration files:

php artisan vendor:publish --provider="PHPDominicana\Reactify\ReactifyServiceProvider"

Then run the migrations:

php artisan migrate

Step 3: Make Your Model Reactionable

You can make any model (like Post, Product, etc.) reactionable by using the HasReaction trait:

use PHPDominicana\Reactify\Traits\HasReaction;

class Post extends Model
{
    use HasReaction;
}

Now your model can receive reactions!

Step 4: Add Reaction Functionality in Controller

Let’s assume you want to let users react on a post. Here's a simple controller method:

public function toggle(Request $request)
{
    $request->validate([
        'reactable_id' => 'required|integer',
        'reactable_type' => 'required|string',
        'type' => ['required', new Enum(Reaction::class)],
    ]);

    $reactableType = $request->input('reactable_type');
    $reactableId = $request->input('reactable_id');
    $type = Reaction::from($request->input('type'));

    // Get the reactable model
    $reactable = $reactableType::findOrFail($reactableId);

    // Toggle the reaction
    $reactable->react(Auth::user(), $type);

    // Return the updated reaction counts
    return response()->json([
        'success' => true,
        'reactions' => [
            'like' => $reactable->reactions(Reaction::LIKE),
            'love' => $reactable->reactions(Reaction::LOVE),
            'laugh' => $reactable->reactions(Reaction::LAUGH),
            'wow' => $reactable->reactions(Reaction::WOW),
            'sad' => $reactable->reactions(Reaction::SAD),
            'angry' => $reactable->reactions(Reaction::ANGRY),
        ],
        'user_reactions' => $reactable->getUserReactions(Auth::user()),
    ]);
}

Step 5: Display Reactions in Blade

In your post.blade.php (or wherever you're showing the post):

@props(['model'])

<div x-data="{
    reactions: {},
    userReactions: [],
    loading: true,
    error: null,

    init() {
        @auth
        this.fetchReactions();
        @else
        this.loading = false;
        @endauth
    },

    fetchReactions() {
        this.loading = true;
        fetch('/api/reactions', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': '{{ csrf_token() }}'
            },
            body: JSON.stringify({
                reactable_id: {{ $model->id }},
                reactable_type: 'App\\Models\\{{ class_basename($model) }}'
            })
        })
        .then(response => response.json())
        .then(data => {
            this.reactions = data.reactions;
            this.userReactions = data.user_reactions;
            this.loading = false;
        })
        .catch(error => {
            this.error = 'Failed to load reactions';
            this.loading = false;
            console.error('Error:', error);
        });
    },

    toggleReaction(type) {
        fetch('/api/reactions/toggle', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': '{{ csrf_token() }}'
            },
            body: JSON.stringify({
                reactable_id: {{ $model->id }},
                reactable_type: 'App\\Models\\{{ class_basename($model) }}',
                type: type
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                this.reactions = data.reactions;
                this.userReactions = data.user_reactions;
            }
        })
        .catch(error => {
            console.error('Error:', error);
        });
    },

    isReacted(type) {
        return this.userReactions.includes(type);
    }
}" class="mt-8 mb-8">
    <h3 class="text-lg font-semibold mb-2">Reactions</h3>

    <div x-show="loading" class="text-gray-500">Loading reactions...</div>

    <div x-show="error" x-text="error" class="text-red-500"></div>

    @auth
    <div x-show="!loading && !error" class="flex flex-wrap gap-2">
        <!-- Like -->
        <button
            @click="toggleReaction('like')"
            :class="{ 'bg-blue-100 border-blue-300': isReacted('like'), 'hover:bg-gray-100': !isReacted('like') }"
            class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors">
            <span class="text-xl">👍</span>
            <span x-text="reactions['like'] || 0" class="text-sm font-medium"></span>
        </button>

        <!-- Love -->
        <button
            @click="toggleReaction('love')"
            :class="{ 'bg-red-100 border-red-300': isReacted('love'), 'hover:bg-gray-100': !isReacted('love') }"
            class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors">
            <span class="text-xl">❤️</span>
            <span x-text="reactions['love'] || 0" class="text-sm font-medium"></span>
        </button>

        <!-- Laugh -->
        <button
            @click="toggleReaction('laugh')"
            :class="{ 'bg-yellow-100 border-yellow-300': isReacted('laugh'), 'hover:bg-gray-100': !isReacted('laugh') }"
            class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors">
            <span class="text-xl">😄</span>
            <span x-text="reactions['laugh'] || 0" class="text-sm font-medium"></span>
        </button>

        <!-- Wow -->
        <button
            @click="toggleReaction('wow')"
            :class="{ 'bg-purple-100 border-purple-300': isReacted('wow'), 'hover:bg-gray-100': !isReacted('wow') }"
            class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors">
            <span class="text-xl">😮</span>
            <span x-text="reactions['wow'] || 0" class="text-sm font-medium"></span>
        </button>

        <!-- Sad -->
        <button
            @click="toggleReaction('sad')"
            :class="{ 'bg-gray-100 border-gray-300': isReacted('sad'), 'hover:bg-gray-100': !isReacted('sad') }"
            class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors">
            <span class="text-xl">😢</span>
            <span x-text="reactions['sad'] || 0" class="text-sm font-medium"></span>
        </button>

        <!-- Angry -->
        <button
            @click="toggleReaction('angry')"
            :class="{ 'bg-orange-100 border-orange-300': isReacted('angry'), 'hover:bg-gray-100': !isReacted('angry') }"
            class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors">
            <span class="text-xl">😠</span>
            <span x-text="reactions['angry'] || 0" class="text-sm font-medium"></span>
        </button>
    </div>
@else
    <div x-show="!loading && !error" class="text-gray-500 mt-2">
        <a href="{{ route('login') }}" class="text-blue-500 hover:underline">Log in</a> to react to this post.
    </div>
@endauth

    @auth
    <div x-show="!loading && Object.keys(reactions).length === 0" class="text-gray-500 mt-2">
        Be the first to react to this post!
    </div>
    @endauth
</div>

Step 6: Add AlpineJS to allow reaction without reloading the page

<!-- In the head of the pag -->
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

Step 7: Add the getReactions method to the reaction controller

public function getReactions(Request $request)
{
    $request->validate([
        'reactable_id' => 'required|integer',
        'reactable_type' => 'required|string',
    ]);

    $reactableType = $request->input('reactable_type');
    $reactableId = $request->input('reactable_id');

    // Get the reactable model
    $reactable = $reactableType::findOrFail($reactableId);

    return response()->json([
        'reactions' => [
            'like' => $reactable->reactions(Reaction::LIKE),
            'love' => $reactable->reactions(Reaction::LOVE),
            'laugh' => $reactable->reactions(Reaction::LAUGH),
            'wow' => $reactable->reactions(Reaction::WOW),
            'sad' => $reactable->reactions(Reaction::SAD),
            'angry' => $reactable->reactions(Reaction::ANGRY),
        ],
        'user_reactions' => $reactable->getUserReactions(Auth::user()),
    ]);
}

Conclusion

Reactify package makes it super easy to add reaction features to your Laravel application. With just a few steps, you’ve got a fully working reaction system!

Reactions

Loading reactions...
Log in to react to this post.

Comments

Please login to leave a comment.

Newsletter