Library / SDK
laravel-notification-channels/fcm avatar
laravel-notification-channels/fcm

Laravel FCM channel: Firebase Cloud Messaging behind the notification API

Firebase Cloud Messaging (FCM) notifications channel for Laravel

610 stars133 forksPHPMIT

At a glance

What is it?
The laravel-notification-channels/fcm package adds an FCM channel to Laravel's notification system, wrapping the laravel-firebase library so you can send to a device token, a topic or a multicast list from a Notification class. It also exposes the raw Firebase payload, which is where the real control sits and where the real complexity is.
Who is it for?
Adopt the FCM channel if your Laravel app already sends notifications and you need to add push to that same Notification class rather than bolting a separate SDK call onto your services. Do not adopt it if you need features outside the FCM API surface it exposes, since it is a channel wrapper and not a general Firebase client, and note that the underlying client is laravel-firebase, so its configuration and version constraints apply to you as well.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A channel, not a Firebase client, and the package that does the work

This package makes it easy to send notifications using Firebase Cloud Messaging with Laravel. The name of the thing tells you the shape of it: a notification channel, one of a family of Laravel notification channels hosted under the laravel-notification-channels organisation.

The key implementation detail is in the setup section. The package uses the laravel-firebase library to authenticate and make the API calls to Firebase, and you are told to follow the configuration steps in that library's own readme before using this one. Then you need to specify FIREBASE_CREDENTIALS in your .env file.

That delegation is the important architectural fact. This package is not a Firebase SDK and it does not implement the FCM protocol. It is an adapter that maps Laravel's notification concepts onto the messaging client provided by kreait/laravel-firebase. The consequences are mostly good, since authentication, token handling and HTTP behaviour come from a library that maintains them, and you inherit that maintenance rather than owning it.

The consequence to plan for is version coupling. Your Firebase client version is whatever laravel-firebase pins, and the notification channel sits on top of it. When you upgrade one, you upgrade the other.

The package is MIT licensed, published on Packagist as laravel-notification-channels/fcm, and the last push to the repository was on 2026-10-05. The release history shows a major version jump, from 4.1.0 in December 2023 to 6.0.0 on 2025-11-17, whose release title is Topic Notifications, which tells you the topic support was a headline feature of that release rather than something that has always been there.

Writing the notification class and routing it to a token

The usage pattern is the standard Laravel notification pattern. A Notification class declares the channel in via and builds the message in toFcm.

php
use Illuminate\Notifications\Notification;
use NotificationChannels\Fcm\FcmChannel;
use NotificationChannels\Fcm\FcmMessage;
use NotificationChannels\Fcm\Resources\Notification as FcmNotification;

class AccountActivated extends Notification
{
    public function via($notifiable)
    {
        return [FcmChannel::class];
    }

    public function toFcm($notifiable): FcmMessage
    {
        return (new FcmMessage(notification: new FcmNotification(
                title: 'Account Activated',
                body: 'Your account has been activated.',
                image: 'http://example.com/url-to-image-here.png'
            )))
            ->data(['data1' => 'value', 'data2' => 'value2'])
            ->custom([
                'android' => [
                    'notification' => [
                        'color' => '#0A0A0A',
                        'sound' => 'default',
                    ],
                    'fcm_options' => [
                        'analytics_label' => 'analytics',
                    ],
                ],
                'apns' => [
                    'payload' => [
                        'aps' => [
                            'sound' => 'default'
                        ],
                    ],
                    'fcm_options' => [
                        'analytics_label' => 'analytics',
                    ],
                ],
            ]);
    }
}

Three layers are visible in that message. The notification resource carries the title, the body and an image, and it is aliased on import because the class name Notification would collide with the Laravel notification base class. The data method attaches a key value payload that arrives with the message rather than being displayed. The custom method takes the raw platform payload, and that is where the android and apns blocks live, each with a notification section and an fcm_options section carrying an analytics label.

The delivery target comes from the notifiable model. You have to set a routeNotificationForFcm method, and it should return the user's FCM token or tokens from storage:

php
class User extends Authenticatable
{
    use Notifiable;

    ...

    public function routeNotificationForFcm()
    {
        return $this->fcm_token;
    }
}

Returning an array of tokens sends the notification as a multicast to different user devices, which the documentation shows by returning getDeviceTokens() instead of a single column. The docblock on the method says the return type is string or array, so the model decides which.

Once that is in place, sending is the ordinary Laravel call:

php
$user->notify(new AccountActivated);

Topics, custom clients and the full message builder

Topics get their own channel. You send to a topic with the FcmTopicChannel, either through an on-demand notification routed to the topic, or by setting the topic on the message itself in the toFcm method:

php
use NotificationChannels\Fcm\FcmTopicChannel;

Notification::route(FcmTopicChannel::class, 'news')
    ->notify(new BlogCreated($blog));

The on-demand form is the one to note, because it means a notification class does not need to know it is going to a topic. The route call names the channel and the topic, and the same BlogCreated notification that goes to a user can go to a topic. That is what makes the topic feature useful for things like a news or broadcast feed.

The message builder itself has a fluent surface, and the documentation points at the FcmMessage source for the complete list:

php
FcmMessage::create()
    ->name('name')
    ->token('token')
    ->topic('topic')
    ->condition('condition')
    ->data(['a' => 'b'])
    ->custom(['notification' => []]);

The four target setters are name, token, topic and condition, which map onto the different ways FCM addresses a destination. Having all four on one builder means the message object decides how it is addressed rather than the calling code.

The custom clients section adds a level of control for anyone who needs it. You can change the underlying Firebase Messaging client on the fly, by providing an instance of Kreait\Firebase\Contract\Messaging to your FCM message, and it will be used in place of the default client:

php
public function toFcm(mixed $notifiable): FcmMessage
{
    $client = app(\Kreait\Firebase\Contract\Messaging::class);

    return FcmMessage::create()->usingClient($client);
}

The contract is the interface, not the concrete class, so the container resolves whatever implementation you have bound. That is the escape hatch for a project that needs a different client configuration, a mock in tests, or a client wired to a specific project.

One thing to notice is the type hint in that signature, mixed $notifiable, while the earlier example used a plain $notifiable with a return type of FcmMessage. The newer form is what you would write in a project on a recent PHP, and it suggests the package supports both eras of the Laravel notification signature.

Failed notifications, expired tokens and the listener that cleans up

The error handling section is the part of any push integration that gets skipped and then regretted, and this package gives you the hook you need.

When a notification fails, the package dispatches an Illuminate\Notifications\Events\NotificationFailed event. You listen for it and handle the notification as appropriate. The example given is the obvious one, deleting expired notification tokens from your database:

php
<?php

namespace App\Listeners;

use Illuminate\Notifications\Events\NotificationFailed;
use Illuminate\Support\Arr;

class DeleteExpiredNotificationTokens
{
    public function handle(NotificationFailed $event): void
    {
        if ($event->channel == FcmChannel::class) {

            $report = Arr::get($event->data, 'report');

            $target = $report->target();

            $event->notifiable->notificationTokens()
                ->where('push_token', $target->value())
                ->delete();
        }
    }
}

Read the shape of that carefully. The listener checks the channel first, so it does not act on failures from other channels. It pulls a report out of the event data with Arr::get, and takes the target from that report, and then matches the target's value against the push_token column of the notifiable's notification tokens relation. The token that failed is the token that gets removed.

That is the mechanism that keeps a push integration healthy over time. Device tokens expire, a user reinstalls an app, and the old token keeps failing forever. Without this listener you accumulate dead tokens and every send to that user fails.

The listener still has to be registered, and the documentation is explicit about it: remember to register your event listeners in the event service provider. The mapping goes in the listen property:

php
protected $listen = [
    \Illuminate\Notifications\Events\NotificationFailed::class => [
        \App\Listeners\DeleteExpiredNotificationTokens::class,
    ],
];

The channel check inside the listener plus the registration in the provider are both required, and missing either is a silent failure rather than an error.

The shape of the package and where to look for the rest

The repository is small and conventional for a Laravel channel. The top level holds an src directory for the package code, a tests directory, a phpunit.xml.dist, a composer.json, a CHANGELOG.md, a CONTRIBUTING.md, a LICENSE.md, and the StyleCI and editor configuration files. There is a .github directory for the workflows.

Testing is a single Composer script:

bash
composer test

The README has a contents list that maps to the sections it covers, including a Changelog section that points at the CHANGELOG file for what has changed recently, and a Security section that is cut off in the text available here.

The notable thing about the release history is the version numbering. The listed releases are 6.0.0 on 2025-11-17 with the title Topic Notifications, 4.1.0 on 2023-12-24, and 4.0.0 on 2023-10-29. A jump from 4.x to 6.x without a 5.x in the list suggests the major version tracks breaking changes in the message API rather than the Laravel version, and the 6.0.0 title tells you that the topic support and the on-demand routing were the headline change.

What the package deliberately does not do is implement FCM itself. It has no opinion about how you obtain a device token in your app, no code for registering a token with your backend, and no opinion about the Firebase admin SDK beyond delegating to laravel-firebase. The token lifecycle is yours: your mobile app registers a token, stores it, and your routeNotificationForFcm reads it.

That boundary is the right one for a channel, and it also tells you what to check first when you adopt it. The integration is complete when the notifiable returns a token, the token is current, and the failed notification listener is registered.

Editorial conclusion

Adopt the FCM channel if your Laravel app already sends notifications and you need to add push to that same Notification class rather than bolting a separate SDK call onto your services. Do not adopt it if you need features outside the FCM API surface it exposes, since it is a channel wrapper and not a general Firebase client, and note that the underlying client is laravel-firebase, so its configuration and version constraints apply to you as well. Before sending your first real notification, set FIREBASE_CREDENTIALS in your .env file, implement routeNotificationForFcm on your notifiable model, and register a listener for the NotificationFailed event so expired tokens get deleted instead of retried forever.

Frequently asked questions

How do I install the Laravel FCM notification channel?

Install it with composer require laravel-notification-channels/fcm. The package uses the laravel-firebase library to authenticate and make the API calls, so follow that library's configuration steps and set FIREBASE_CREDENTIALS in your .env file.

How do I send an FCM notification from a Laravel Notification class?

Return FcmChannel::class from the via method and build an FcmMessage in the toFcm method, then call $user->notify(new AccountActivated). The notifiable model needs a routeNotificationForFcm method returning the token or an array of tokens for a multicast.

How do I send an FCM notification to a topic in Laravel?

Use the FcmTopicChannel and route the notification on demand with Notification::route(FcmTopicChannel::class, 'news')->notify(new BlogCreated($blog)), or set the topic on the message itself in the toFcm method. Topic support arrived in the 6.0.0 release.

How do I handle failed FCM notifications and remove expired tokens?

The package dispatches an Illuminate\Notifications\Events\NotificationFailed event on failure. Listen for it, check that the channel is FcmChannel::class, pull the report from the event data, and delete the notifiable's notification token matching the target value, then register the listener in the event service provider.

Can I use a custom Firebase messaging client with the FCM channel?

Yes. Provide an instance of Kreait\Firebase\Contract\Messaging to your FCM message with the usingClient method and it will be used in place of the default client, which lets you resolve a different implementation from the container.

Official sources

  1. laravel-notification-channels/fcm on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/laravel-notification-channels-fcm.svg)](https://hysenlabs.com/projects/laravel-notification-channels-fcm)

Was this useful?