# DataFirefly Live Counters

> DataFirefly Live Counters displays animated counters (customers, orders, social followers, custom KPIs) on your WordPress/WooCommerce site that stay accurate even when the entire site is served from a full-page cache…

- Page: <https://www.datafirefly.com/en/documentation/dflivecounters-wp/>
- Language: en
- Last updated: 2026-06-28
- Other languages: [fr](https://www.datafirefly.com/documentation/dflivecounters-wp/index.md), [es](https://www.datafirefly.com/es/documentation/dflivecounters-wp/index.md), [de](https://www.datafirefly.com/de/documentation/dflivecounters-wp/index.md), [it](https://www.datafirefly.com/it/documentation/dflivecounters-wp/index.md), [pl](https://www.datafirefly.com/pl/documentation/dflivecounters-wp/index.md), [nl](https://www.datafirefly.com/nl/documentation/dflivecounters-wp/index.md), [pt](https://www.datafirefly.com/pt/documentation/dflivecounters-wp/index.md)
- Index: <https://www.datafirefly.com/en/documentation/llms.txt>

DataFirefly Live Counters displays animated counters (customers, orders, social followers, custom KPIs) on your WordPress/WooCommerce site that stay accurate even when the entire site is served from a full-page cache like LiteSpeed Cache or WP Rocket.

## Installation and quick start

### Prerequisites

- WordPress 6.2 or above (tested on 6.7).
- PHP 8.1 or above.
- WooCommerce 7.0+ recommended for store counters. Without WooCommerce, social and custom KPI counters stay fully functional.
- Polylang or WPML optional for multilingual labels.

### Installation

1. Download `dflivecounters.zip` from your DataFirefly account.
2. In WordPress, go to **Plugins → Add New → Upload Plugin**.
3. Choose the zip and click **Install Now**.
4. Activate the plugin. A new **Live Counters** item appears under WooCommerce (or Settings if WooCommerce isn't installed).

### First display in 30 seconds

Drop this shortcode into any page or post:

```
[dflivecounters]
```

On first load, a grid of four counters appears with a count-up animation. Numbers are computed from your WooCommerce catalog and cached.

## General settings

Go to **WooCommerce → Live Counters**. The page has four cards, followed by a live preview and a cache-flush button.

### "Display & cache" card

- **Style**: `Cards`, `Minimal`, `Gradient`.
- **Columns**: 1 to 6. Responsive: 2 columns on mobile, 1 on very small screens.
- **Accent color**: used for icons, numbers (cards) and background (gradient). Default `#0f172a`.
- **Animation duration (ms)**: 200 to 8,000. 0 disables animation.
- **Counter cache (min)**: 60 by default.
- **Social cache (min)**: 360 by default (social APIs are rate-limited).
- **Abbreviate large numbers**: displays `12.4k` instead of `12,400`.
- **Add "+" to cumulative counters**.
- **Pre-warm cache automatically (cron)**: Action Scheduler preferred, WP-Cron fallback.

### Counted order statuses

- **Counted statuses (customers, orders, items, countries)**: defaults to `Processing` and `Completed`.
- **"Shipped" statuses**: used only for _Products shipped_. Defaults to `Completed`.

### Activity start date

The _Years in business_ counter is computed from the date set in **"Founding date"**.

### Clear and rebuild cache

The **"↻ Clear and rebuild cache now"** button removes all plugin transients and triggers an immediate warm-up. Any settings change automatically flushes the counter cache and reschedules the warm-up.

## WooCommerce counters

### Available counters

- **Customers served** (`customers`): distinct billing emails on orders in a counted status.
- **Products shipped** (`shipped`): sum of item quantities on orders in a "shipped" status.
- **Orders processed** (`orders`).
- **Items sold** (`items_sold`): sum of quantities across all order line items.
- **Products in catalog** (`products`).
- **Customer reviews** (`reviews`): approved reviews.
- **Countries shipped to** (`countries`).
- **Years in business** (`years`): from the founding date.

### Enable and customize a counter

In the **"Store counters"** table, tick _Active_ and optionally:

- Set a **custom label**.
- Choose a **time window** (see dedicated section).
- Add an **offset** to integrate older history (e.g. 1,200 customers from a previous store).
- Set a **goal** to activate the progress bar.

### HPOS compatibility

The plugin auto-detects HPOS (High-Performance Order Storage) or legacy (CPT) storage. Queries are written in two optimized flavors. HPOS and Cart/Checkout Blocks compatibility is declared via the `before_woocommerce_init` hook.

## Social and custom KPI counters

### Supported social networks

- **Facebook** and **Instagram** — automatic retrieval via the Meta Graph API v19 (Instagram for Business or Creator accounts only).
- **TikTok**, **X (Twitter)**, **LinkedIn**, **YouTube** — manual entry.

TikTok, X and LinkedIn don't expose a follower counter through a reliable public API, hence the manual entry.

### Configure Facebook or Instagram via the Meta Graph API

1. Create an app at [developers.facebook.com](https://developers.facebook.com).
2. Generate a long-lived access token with the `pages_read_engagement` permission (Facebook) or `instagram_basic` + `pages_show_list` (Instagram).
3. Get the Facebook Page ID or IG Business ID.
4. In the "Social networks" card, tick **"Fetch via API"**, paste the ID into _Object ID_ and the token into _Access token_.

If the API call fails (expired token, rate limit), the plugin keeps the last known value. The "manual" field serves as the ultimate fallback.

### Custom KPI counters

In the **"Custom KPI counters"** card, click **"+ Add counter"**, then fill: icon (users, award, heart, leaf, download…), label, value, optional prefix ("$", "+"), optional suffix ("%, h, M"), optional goal.

## Time windows, goals and trend

### Time windows per counter

Counters that aggregate over time (`customers`, `shipped`, `orders`, `items_sold`, `reviews`, `countries`) can be restricted:

- **All-time**: default behavior.
- **This year**: since January 1st.
- **This month**: since the 1st of the month.
- **Last 30 days**: rolling window.

The effect "124 orders this month" is often more engaging than "9,421 orders".

### Goals and progress bar

Setting a goal in the **"Goal (0 = none)"** column automatically activates a bar under the counter, animated alongside the count-up, up to `min(100%, value / goal)`. Also available on social and custom counters.

### Trend indicator ▲/▼

A colored chip appears next to the number:

- ▲ green if the value increased since the previous snapshot.
- ▼ red if it decreased.
- No chip if the change is null or history is insufficient.

The percentage is computed on a 7-day rolling window by default, configurable via the `dflc_trend_window` filter. Baselines are stored in a single WordPress option (`dflc_trend`).

**Expected patience.** On a new site, the trend chip only appears after the window has elapsed (7 days by default).

## Display: block, widget, shortcode

### Gutenberg block

In the block editor, search for **"DataFirefly Live Counters"** (Widgets category). The inspector panel lets you configure columns, style and the list of counters to display. The preview uses `ServerSideRender`, identical to the front render.

### Classic widget

Under **Appearance → Widgets**, add **"DataFirefly Live Counters"**. Fields: title, columns (0 to 6), style, keys (comma-separated list, empty = all enabled counters).

### Shortcode

```
[dflivecounters]
[dflivecounters keys="customers,orders,reviews" columns="3"]
[dflivecounters keys="social_facebook,social_instagram" columns="2" style="gradient"]
[dflivecounters keys="custom_0,custom_1" style="minimal"]
```

Supported attributes: `keys` (string, key list), `columns` (int 1-6), `style` (`cards`, `minimal`, `gradient`).

### Available keys

| Counter | Key |
| --- | --- |
| Customers | `customers` |
| Products shipped | `shipped` |
| Orders processed | `orders` |
| Items sold | `items_sold` |
| Products in catalog | `products` |
| Customer reviews | `reviews` |
| Countries shipped to | `countries` |
| Years in business | `years` |
| Social networks | `social_facebook`, `social_instagram`, `social_tiktok`, `social_twitter`, `social_linkedin`, `social_youtube` |
| Custom counters | `custom_0`, `custom_1`, etc. |

### Theme insertion (PHP)

```
echo do_shortcode( '[dflivecounters keys="customers,orders" columns="2"]' );
```

## Cache architecture

### The problem

When a page cache (LiteSpeed, WP Rocket, NGINX micro-cache, Varnish, Cloudflare APO) serves pre-generated HTML, any number rendered in PHP is frozen. The classic answer — disabling the cache on these pages — seriously hurts performance.

### The principle: separate structure and values

- **Cacheable structure**: grid, icons, labels and empty slots, server-rendered, perfectly cacheable.
- **Non-cacheable values**: fetched via `fetch()` to a dedicated REST route with a short `Cache-Control` header.

The visitor receives the cached HTML instantly, then numbers fill in JavaScript with a count-up animation.

### Transient cache + warm-up

The REST route never runs a heavy SQL query during a visit. It reads WordPress transients, pre-warmed by Action Scheduler (`dflc_warm_cache` job) or WP-Cron as fallback.

### Stale-while-revalidate

If a transient expires right between two warm-ups:

1. The **last known value** is read from the persistent option `dflc_lastgood` (which survives transient expiration) and returned immediately.
2. An asynchronous refresh job is queued.
3. A short lock (2 minutes) prevents stacking concurrent refreshes.

True cold start only happens at the very first display after installation.

### REST endpoint security

- Public read-only.
- Key whitelist: only keys matching actually configured counters are accepted.
- `Cache-Control: public, max-age=...` header sized on the counter TTL.

**Caveat.** If your CDN ignores the `Cache-Control` on the REST route and caches it aggressively, numbers will be frozen at the CDN level. Exclude `/wp-json/dflivecounters/v1/counters` from your CDN cache if you observe this.

## Developer API

Five PHP filters extend the plugin without touching the core.

### dflc_counter_definitions

Add, reorder or hide counters.

```
add_filter( 'dflc_counter_definitions', static function ( array $items, array $settings ) {
    $items[] = array(
        'key'        => 'newsletter_subscribers',
        'label'      => 'Newsletter subscribers',
        'icon'       => 'heart',
        'suffix'     => '+',
        'prefix'     => '',
        'abbreviate' => true,
        'goal'       => 5000,
    );
    return $items;
}, 10, 2 );
```

### dflc_compute

Short-circuits the calculation. Returning an integer takes over, `null` lets the core handle it.

```
add_filter( 'dflc_compute', static function ( $pre, string $key, array $settings ) {
    if ( 'newsletter_subscribers' === $key ) {
        return (int) get_option( 'my_newsletter_count', 0 );
    }
    return $pre;
}, 10, 3 );
```

### dflc_counter_value

Filters the final value just before sending to the front.

```
add_filter( 'dflc_counter_value', static function ( int $value, string $key ) {
    if ( 'customers' === $key && $value < 1000 ) {
        return 1000;
    }
    return $value;
}, 10, 2 );
```

### dflc_payload

Filters the entire REST payload.

```
add_filter( 'dflc_payload', static function ( array $payload, array $only, bool $force ) {
    foreach ( $payload as &$item ) {
        $item['emoji'] = '🎉';
    }
    return $payload;
}, 10, 3 );
```

### dflc_trend_window

Changes the trend rolling window. Value in seconds, default `7 * DAY_IN_SECONDS`.

```
add_filter( 'dflc_trend_window', static fn() => 30 * DAY_IN_SECONDS );
```

### Recipe: Mailchimp counter

```
add_filter( 'dflc_counter_definitions', static function ( array $items ) {
    $items[] = array(
        'key' => 'mailchimp_subs', 'label' => 'Newsletter subscribers',
        'icon' => 'heart', 'suffix' => '+', 'prefix' => '',
        'abbreviate' => true, 'goal' => 0,
    );
    return $items;
} );

add_filter( 'dflc_compute', static function ( $pre, string $key ) {
    if ( 'mailchimp_subs' !== $key ) {
        return $pre;
    }
    $response = wp_remote_get( 'https://us1.api.mailchimp.com/3.0/lists/LIST_ID', array(
        'headers' => array( 'Authorization' => 'Bearer ' . MAILCHIMP_API_KEY ),
    ) );
    if ( is_wp_error( $response ) ) {
        return 0;
    }
    $body = json_decode( wp_remote_retrieve_body( $response ), true );
    return (int) ( $body['stats']['member_count'] ?? 0 );
}, 10, 2 );
```

## Troubleshooting

### Numbers don't animate

- Verify that `dflc-front.js` loads (browser network tab).
- Verify the REST route `/wp-json/dflivecounters/v1/counters` answers with 200 and valid JSON.
- Test with another theme.

### Numbers always display "—"

- The REST call failed. Check the JS console.
- The REST route may be blocked by a security plugin.

### Numbers don't update

- Check that **"Pre-warm cache automatically"** is enabled.
- On low-traffic sites, consider a real system cron instead of WP-Cron.
- Force a refresh via the **"↻ Clear and rebuild cache now"** button.

### Meta API call returns 0

- Verify token validity on the [Access Token Debugger](https://developers.facebook.com/tools/debug/accesstoken/).
- Verify the Instagram account is Business or Creator.

### WP 6.7 "_load_textdomain_just_in_time" notice

Fixed in version 1.1.1.
