# Top Sellers of the Last 30 Days Widget

> Adds a dashboard widget listing your five best-selling products of the past 30 days by units sold, with links to edit each product.

- Type: PHP
- Runs: Admin only
- Category: [WooCommerce](https://wpide.com/library/category/woocommerce/)
- Requires: WooCommerce
- Tags: woocommerce, dashboard, reports, products
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/woo-dashboard-top-sellers/

## Code

```php
add_action( 'wp_dashboard_setup', function () {
    if ( ! current_user_can( 'view_woocommerce_reports' ) ) {
        return;
    }

    wp_add_dashboard_widget( 'wpide_top_sellers', 'Top sellers (30 days)', function () {
        global $wpdb;

        $rows = get_transient( 'wpide_top_sellers' );

        if ( false === $rows ) {
            $since = gmdate( 'Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS );
            $hpos  = class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
            $join  = $hpos
                ? "INNER JOIN {$wpdb->prefix}wc_orders o ON o.id = oi.order_id AND o.type = 'shop_order' AND o.status IN ('wc-processing','wc-completed') AND o.date_created_gmt >= %s"
                : "INNER JOIN {$wpdb->posts} o ON o.ID = oi.order_id AND o.post_type = 'shop_order' AND o.post_status IN ('wc-processing','wc-completed') AND o.post_date_gmt >= %s";

            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join contains only table names and a placeholder.
            $rows = $wpdb->get_results( $wpdb->prepare(
                "SELECT pid.meta_value AS product_id, SUM(qty.meta_value) AS units
                 FROM {$wpdb->prefix}woocommerce_order_items oi
                 {$join}
                 INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta pid ON pid.order_item_id = oi.order_item_id AND pid.meta_key = '_product_id'
                 INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta qty ON qty.order_item_id = oi.order_item_id AND qty.meta_key = '_qty'
                 WHERE oi.order_item_type = 'line_item'
                 GROUP BY pid.meta_value ORDER BY units DESC LIMIT 5",
                $since
            ) );
            set_transient( 'wpide_top_sellers', $rows, HOUR_IN_SECONDS );
        }

        if ( ! $rows ) {
            echo '<p>' . esc_html( 'No sales in the last 30 days.' ) . '</p>';
            return;
        }

        echo '<ol>';

        foreach ( $rows as $row ) {
            printf( '<li><a href="%1$s">%2$s</a> &ndash; %3$s</li>', esc_url( get_edit_post_link( $row->product_id ) ), esc_html( get_the_title( $row->product_id ) ), esc_html( sprintf( '%s sold', wc_stock_amount( $row->units ) ) ) );
        }

        echo '</ol>';
    } );
} );
```

## Notes

Counts processing and completed orders, caches the list for an hour and works with HPOS and legacy order storage.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Top Sellers of the Last 30 Days Widget" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
