How to Build a Custom "Flash Sale" Module in Magento 2

How to Build a Custom "Flash Sale" Module in Magento 2

Flash sales are a powerful way to create urgency, boost conversions, and clear out inventory quickly. But if you're running a Magento 2 store, you might find that the built-in promotions don’t offer the flexibility you need for a true flash sale experience. That’s where a custom module comes in.

In this guide, we’ll walk through building a custom flash sale module from scratch. We’ll cover everything from setting up the database structure to integrating with existing Magento features like cart rules and free shipping. Plus, we’ll share best practices to ensure your flash sales run smoothly—even under heavy traffic.

Why Build a Custom Flash Sale Module?

Magento 2 has built-in promotions (like catalog price rules and cart rules), but they lack some key flash sale features:

  • Countdown timers – Built-in rules don’t show urgency.
  • Limited-time availability – You can set dates, but not real-time stock-based triggers.
  • Dynamic pricing – Adjusting discounts based on remaining stock or time left.

A custom module lets you add these features while keeping full control over performance and user experience.

Step 1: Setting Up the Module Structure

First, create the basic module structure in app/code/Magefine/FlashSale:

Magefine/FlashSale/  
├── etc/  
│   ├── module.xml  
│   └── db_schema.xml  
├── Controller/  
│   └── Adminhtml/  
│       └── Index/  
│           └── Index.php  
├── Model/  
│   ├── FlashSale.php  
│   └── ResourceModel/  
│       └── FlashSale.php  
├── view/  
│   └── adminhtml/  
│       ├── layout/  
│       └── ui_component/  
└── registration.php  

Define the module in registration.php:

<?php  
\Magento\Framework\Component\ComponentRegistrar::register(  
    \Magento\Framework\Component\ComponentRegistrar::MODULE,  
    'Magefine_FlashSale',  
    __DIR__  
);  

Step 2: Database Setup

Create a table to store flash sales in db_schema.xml:

<table name="magefine_flashsale" resource="default" engine="innodb">  
    <column xsi:type="int" name="flashsale_id" unsigned="true" nullable="false" identity="true"/>  
    <column xsi:type="varchar" name="name" length="255" nullable="false"/>  
    <column xsi:type="datetime" name="start_time" nullable="false"/>  
    <column xsi:type="datetime" name="end_time" nullable="false"/>  
    <column xsi:type="int" name="discount_amount" unsigned="true"/>  
    <column xsi:type="smallint" name="is_active" unsigned="true" nullable="false" default="0"/>  
    <constraint xsi:type="primary" referenceId="PRIMARY">  
        <column name="flashsale_id"/>  
    </constraint>  
</table>  

Run bin/magento setup:upgrade to apply the changes.

Step 3: Creating the Admin Interface

Next, set up a grid in the admin panel using a UI component (view/adminhtml/ui_component/magefine_flashsale_listing.xml):

<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">  
    <argument name="data" xsi:type="array">  
        <item name="js_config" xsi:type="array">  
            <item name="provider" xsi:type="string">magefine_flashsale_listing.magefine_flashsale_listing_data_source</item>  
        </item>  
    </argument>  
    <columns name="flashsale_columns">  
        <column name="name">  
            <argument name="data" xsi:type="array">  
                <item name="config" xsi:type="array">  
                    <item name="filter" xsi:type="string">text</item>  
                    <item name="label" xsi:type="string" translate="true">Name</item>  
                </item>  
            </argument>  
        </column>  
        <!-- Add more columns (start_time, end_time, etc.) -->  
    </columns>  
</listing>  

Step 4: Frontend Integration

To display active flash sales on the frontend, create a block that checks for active promotions:

<?php  
namespace Magefine\FlashSale\Block;  

use Magento\Framework\View\Element\Template;  

class FlashSale extends Template  
{  
    protected $_flashSaleFactory;  

    public function __construct(  
        \Magento\Framework\View\Element\Template\Context $context,  
        \Magefine\FlashSale\Model\FlashSaleFactory $flashSaleFactory,  
        array $data = []  
    ) {  
        $this->_flashSaleFactory = $flashSaleFactory;  
        parent::__construct($context, $data);  
    }  

    public function getActiveFlashSales()  
    {  
        $now = new \DateTime();  
        $collection = $this->_flashSaleFactory->create()->getCollection()  
            ->addFieldToFilter('is_active', 1)  
            ->addFieldToFilter('start_time', ['lteq' => $now->format('Y-m-d H:i:s')])  
            ->addFieldToFilter('end_time', ['gteq' => $now->format('Y-m-d H:i:s')]);  

        return $collection;  
    }  
}  

Step 5: Performance Optimization

Flash sales attract heavy traffic. Optimize your module with:

  • Full-Page Cache (FPC) compatibility – Use placeholders for dynamic content.
  • Redis caching – Store flash sale data in Redis to reduce database load.
  • Queue-based stock updates – Use message queues to handle inventory changes.

Case Study: Boosting Conversions by 40%

One of our clients saw a 40% increase in conversions after implementing a custom flash sale module with:

  • Real-time countdown timers
  • Dynamic pricing (discounts increased as stock decreased)
  • Integration with cart rules for free shipping

By following this guide, you can build a similar solution tailored to your store’s needs.

Got questions? Drop them in the comments below!