foxelabs/wp-flash-notices

WordPress admin notices as flash notices, stored with the transient API so they render once after a page reload, with swappable storage and rendering drivers.

Maintainers

Package info

github.com/foxelabs/wp-flash-notices

pkg:composer/foxelabs/wp-flash-notices

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 2

2.0.0 2026-08-04 17:26 UTC

This package is auto-updated.

Last update: 2026-08-04 18:28:26 UTC


README

WP Flash Notices is a small WordPress library that turns admin notices into flash notices: you queue a notice during one request (typically right before a redirect), and it is printed on the next page load and then cleared. It borrows the idea from framework flash messages and implements it on top of the WordPress transient API.

Notices queued during a request are held in memory and flushed to storage once, on shutdown, so queueing ten notices still costs a single database write. Multisite network notices are kept in a separate queue, and both the storage and the rendering layers are swappable.

📖 Full documentation: docs.foxelabs.com

Requirements

  • PHP 7.4 or higher
  • WordPress 6.0+
  • Composer

Installation

composer require foxelabs/wp-flash-notices

The library autoloads under the FoxeLabs\Notices\ namespace via PSR-4.

Architecture

The library is organised as a tiny container wired up by the entry class FoxeLabs\Notices\Notices. The folder layout mirrors the namespace:

src/
├── Notices.php                      # Container + entry point
├── Contracts/
│   ├── StoreInterface.php
│   └── RendererInterface.php
├── Storage/
│   └── TransientStore.php           # (site_)transient wrapper, separate site + network queues
├── Rendering/
│   └── AdminNoticeRenderer.php      # Core admin notice markup
├── Support/
│   ├── KeyPrefixer.php              # Shared transient key + hook name prefixing
│   └── Notice.php                   # Immutable notice value object
└── Exceptions/
    └── NoticeException.php

Services receive their collaborators by constructor injection so they can be unit-tested without WordPress in the loop. Construction has no side effects — call register() to attach the hooks.

Usage

Initialisation

Each instance is scoped to a single prefix. Every transient key and every hook name is namespaced under it, so two plugins using this library on the same site never see each other's notices:

$notices = \FoxeLabs\Notices\Notices::get_instance( 'my_plugin' );
$notices->register();

Call register() once, early — plugins_loaded is a good place. You can also instantiate directly, which is what you want when injecting your own store or renderer:

$notices = new \FoxeLabs\Notices\Notices( 'my_plugin', $my_store, $my_renderer );

Anywhere else in your plugin, Notices::get_instance( 'my_plugin' ) hands back the same container, so you don't need to pass it around.

Adding a notice

$notices->add( $key, $message, $type = 'info', $dismissible = true, $network = false );
Parameter Type Description
$key string Unique key. Re-using a key replaces the earlier notice.
$message string Notice body. Limited HTML allowed (run through wpautop() + wp_kses()).
$type string success, info, warning or error. Unknown types fall back to info.
$dismissible bool Show the dismiss button.
$network bool Queue as a network admin notice (multisite only).

It returns the queued Notice, and throws NoticeException when the key or message is empty.

// Notice with a green bar.
$notices->add( 'settings-saved', 'Your settings have been saved.', 'success' );

// Notice with a red bar.
$notices->add( 'import-failed', 'The import could not be completed.', 'error' );

// Notice with a yellow bar.
$notices->add( 'license-expiring', 'Your license expires in 3 days.', 'warning' );

// Notice with a blue bar, without a dismiss button.
$notices->add( 'sync-running', 'A sync is currently running.', 'info', false );

// Network admin notice (multisite).
$notices->add( 'network-update', 'All sites have been updated.', 'success', true, true );

Off multisite, a notice queued with $network = true silently falls back to the regular site queue.

Reading and clearing

$notice  = $notices->get( 'settings-saved' ); // ?Notice
$notices->fetch();                            // Notice[] keyed by notice key
$notices->clear();                            // Delete the stored queue

Pass true as the last argument to any of these to work on the network queue.

Notice is an immutable value object with key(), message(), type(), is_dismissible() and to_array().

Front-end notices

Notices are printed automatically on admin_notices and network_admin_notices. To print them in a front-end template, fire the prefixed action yourself:

do_action( 'my_plugin_front_notices' );

Hooks

Every hook name is prefixed with your instance prefix — the examples below assume my_plugin.

Filters

Hook Arguments Purpose
my_plugin_flash_notice_types string[] $types Register custom notice types. A type maps to the notice-{type} CSS class.
my_plugin_flash_notice_item Notice $notice, bool $network Replace a notice just before it is queued. Return a Notice to override.
my_plugin_flash_notices_fetch Notice[] $notices, bool $network Modify the notices read from storage.
my_plugin_flash_notices_auto_render bool $enable, bool $network Return false to stop the library printing notices — nothing is cleared either.
my_plugin_flash_notices_auto_clear bool $enable, bool $network Return false to keep notices after rendering. You must then call clear() yourself.

Actions

Hook Arguments
my_plugin_flash_notices_after_queue Notice $notice, bool $network, Notice[] $queue, Notice[] $network_queue
my_plugin_flash_notices_after_save Notice[] $queue, Notice[] $network_queue
my_plugin_flash_notices_after_render Notice[] $notices, bool $network
my_plugin_flash_notices_after_clear bool $network
my_plugin_front_notices — (fire it yourself to print notices on the front end)

Custom storage and rendering

Implement StoreInterface to change where the queue lives, or RendererInterface to change the markup, and pass your implementation to the constructor:

use FoxeLabs\Notices\Contracts\RendererInterface;
use FoxeLabs\Notices\Support\Notice;

class MyRenderer implements RendererInterface {

	public function render( array $notices ): void {
		foreach ( $notices as $notice ) {
			printf( '<div class="my-toast my-toast--%s">%s</div>', esc_attr( $notice->type() ), esc_html( $notice->message() ) );
		}
	}
}

$notices = new \FoxeLabs\Notices\Notices( 'my_plugin', null, new MyRenderer() );
$notices->register();

TransientStore also takes an expiration (default one day), so an unread queue cannot linger in the options table forever:

use FoxeLabs\Notices\Storage\TransientStore;
use FoxeLabs\Notices\Support\KeyPrefixer;

$store   = new TransientStore( new KeyPrefixer( 'my_plugin' ), HOUR_IN_SECONDS );
$notices = new \FoxeLabs\Notices\Notices( 'my_plugin', $store );

Upgrading from 1.x

Version 2.0.0 is a breaking rewrite. If you are coming from duckdev/wp-flash-notices:

1.x 2.0.0
duckdev/wp-flash-notices foxelabs/wp-flash-notices
DuckDev\WP_Flash_Notices FoxeLabs\Notices\Notices
Constructor took a transient name Constructor takes a prefix; keys are derived from it
Hooks attached in the constructor Call register() explicitly
wp_flash_notices_* hooks {prefix}_flash_notices_* / {prefix}_flash_notice_*
front_notices action {prefix}_front_notices
get() / fetch() returned arrays Return Notice objects (null / [] when absent)
clear_after_render() public method Removed; clearing is internal to render()
Notices stored forever Stored with an expiration (default one day)

Stored 1.x transients are not migrated — any notice left in the old transient at upgrade time is simply not shown.

Two 1.x bugs are fixed along the way: network notices were saved from the wrong queue (so the site queue was written to the network transient), and the auto-clear callback received mismatched arguments, so notices were not reliably cleared after rendering.

Development

composer install
composer test
composer phpcs

License

GPL-2.0-or-later. See LICENSE.