How to Create a Custom "Back in Stock" Notification System in Magento 2

Why You Need a Custom "Back in Stock" Notification System in Magento 2

Running an eCommerce store means dealing with out-of-stock products—it’s inevitable. But what happens when customers miss out on their favorite items? A well-implemented "Back in Stock" notification system keeps shoppers engaged and boosts conversions by alerting them when products are available again.

While Magento 2 has basic stock alerts, a custom solution gives you more control—better branding, automated workflows, and deeper analytics. Let’s build one from scratch!

Step 1: Setting Up the Database Table

First, we need a table to store customer notifications. Create a custom module (let’s call it Magefine_StockAlert) and define the table in Setup/InstallSchema.php:

<?php
namespace Magefine\StockAlert\Setup;

use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;

class InstallSchema implements InstallSchemaInterface
{
    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $installer = $setup;
        $installer->startSetup();

        $table = $installer->getConnection()->newTable(
            $installer->getTable('magefine_stock_alert')
        )->addColumn(
            'alert_id',
            Table::TYPE_INTEGER,
            null,
            ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
            'Alert ID'
        )->addColumn(
            'product_id',
            Table::TYPE_INTEGER,
            null,
            ['unsigned' => true, 'nullable' => false],
            'Product ID'
        )->addColumn(
            'customer_email',
            Table::TYPE_TEXT,
            255,
            ['nullable' => false],
            'Customer Email'
        )->addColumn(
            'status',
            Table::TYPE_SMALLINT,
            null,
            ['nullable' => false, 'default' => 0],
            'Alert Status (0=Pending, 1=Sent)'
        )->addColumn(
            'created_at',
            Table::TYPE_TIMESTAMP,
            null,
            ['nullable' => false, 'default' => Table::TIMESTAMP_INIT],
            'Creation Time'
        )->addIndex(
            $installer->getIdxName('magefine_stock_alert', ['product_id']),
            ['product_id']
        )->setComment('Stock Alert Subscriptions');

        $installer->getConnection()->createTable($table);
        $installer->endSetup();
    }
}

Step 2: Creating the Frontend Form

Add a "Notify Me" button on product pages when an item is out of stock. In your theme’s catalog_product_view.xml:

<referenceContainer name="product.info.form.content">
    <block class="Magefine\StockAlert\Block\Product\View\StockAlert" name="product.stock.alert" template="Magefine_StockAlert::product/view/stock_alert.phtml" after="product.info.addtocart"/>
</referenceContainer>

Then, create the template (stock_alert.phtml):

<?php if (!$block->isProductInStock() && !$block->isCustomerSubscribed()): ?>
    <div class="stock-alert">
        <form method="post" action="<?= $block->getSubmitUrl() ?>" id="stock-alert-form">
            <div class="field email required">
                <label for="email"><?= __('Notify me when available') ?></label>
                <input type="email" name="email" id="stock-alert-email" class="input-text" placeholder="your@email.com" />
            </div>
            <button type="submit" class="action primary"><?= __('Subscribe') ?></button>
        </form>
    </div>
<?php endif; ?>

Step 3: Handling Form Submissions

Create a controller (Controller/Product/Subscribe.php) to save subscriptions:

<?php
namespace Magefine\StockAlert\Controller\Product;

use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\Message\ManagerInterface;
use Magefine\StockAlert\Model\StockAlertFactory;

class Subscribe implements HttpPostActionInterface
{
    protected $resultFactory;
    protected $messageManager;
    protected $stockAlertFactory;

    public function __construct(
        ResultFactory $resultFactory,
        ManagerInterface $messageManager,
        StockAlertFactory $stockAlertFactory
    ) {
        $this->resultFactory = $resultFactory;
        $this->messageManager = $messageManager;
        $this->stockAlertFactory = $stockAlertFactory;
    }

    public function execute()
    {
        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        $productId = $this->getRequest()->getParam('product_id');
        $email = $this->getRequest()->getParam('email');

        try {
            $stockAlert = $this->stockAlertFactory->create();
            $stockAlert->setProductId($productId)
                ->setCustomerEmail($email)
                ->setStatus(0)
                ->save();

            $this->messageManager->addSuccessMessage(__('You will be notified when this product is back in stock!'));
        } catch (\Exception $e) {
            $this->messageManager->addErrorMessage(__('There was an error with your subscription.'));
        }

        $resultRedirect->setUrl($this->_redirect->getRefererUrl());
        return $resultRedirect;
    }
}

Step 4: Sending Notifications Automatically

Create an observer (Observer/ProductSaveAfter.php) to trigger emails when stock updates:

<?php
namespace Magefine\StockAlert\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magefine\StockAlert\Model\ResourceModel\StockAlert\CollectionFactory;
use Magento\CatalogInventory\Api\StockStateInterface;
use Magento\Framework\Mail\Template\TransportBuilder;

class ProductSaveAfter implements ObserverInterface
{
    protected $stockAlertCollection;
    protected $stockState;
    protected $transportBuilder;

    public function __construct(
        CollectionFactory $stockAlertCollection,
        StockStateInterface $stockState,
        TransportBuilder $transportBuilder
    ) {
        $this->stockAlertCollection = $stockAlertCollection;
        $this->stockState = $stockState;
        $this->transportBuilder = $transportBuilder;
    }

    public function execute(Observer $observer)
    {
        $product = $observer->getEvent()->getProduct();
        $stockQty = $this->stockState->getStockQty($product->getId());

        if ($stockQty > 0) {
            $alerts = $this->stockAlertCollection->create()
                ->addFieldToFilter('product_id', $product->getId())
                ->addFieldToFilter('status', 0);

            foreach ($alerts as $alert) {
                $this->sendNotification($alert, $product);
                $alert->setStatus(1)->save();
            }
        }
    }

    protected function sendNotification($alert, $product)
    {
        $transport = $this->transportBuilder
            ->setTemplateIdentifier('stock_alert_notification')
            ->setTemplateOptions(['area' => 'frontend', 'store' => $product->getStoreId()])
            ->setTemplateVars([
                'product' => $product,
                'product_url' => $product->getProductUrl()
            ])
            ->setFromByScope('general')
            ->addTo($alert->getCustomerEmail())
            ->getTransport();

        $transport->sendMessage();
    }
}

Step 5: Creating the Email Template

Add a transactional email template in view/frontend/email/stock_alert.html:

<!--@subject Your product is back in stock! @-->
<!--@vars {
"var product.name":"Product Name",
"var product_url|raw":"Product URL"
} @-->

<p>Hello,</p>
<p>The product you were waiting for is now available!</p>
<p><strong>{{var product.name}}</strong></p>
<p><a href="{{var product_url}}">Click here to purchase now!</a></p>
<p>Thanks for shopping with us!</p>

Final Thoughts

With this custom system, you’ll:

  • ✅ Reduce cart abandonment by keeping customers informed
  • ✅ Increase conversions when products restock
  • ✅ Gain full control over notification design and timing

Want to skip the coding? Check out Magefine’s premium extensions for ready-made solutions with advanced features like SMS alerts and analytics!