Skip to content

Hooks and Filters

WP Stories provides action hooks and filter hooks that developers can use to customize plugin behavior. All hooks listed below are verified in the plugin source code.


Controls whether uploaded story images are automatically optimized (resized and compressed).

Default: true

File: public/class-wp-stories-submit-user-stories.php

add_filter( 'wp_stories_enable_image_optimization', function( $enabled, $attachment_id ) {
// Disable optimization for a specific attachment
return $enabled;
}, 10, 2 );

Sets the JPEG compression quality for optimized story images.

Default: 85

File: public/class-wp-stories-submit-user-stories.php

add_filter( 'wp_stories_image_quality', function( $quality ) {
return 75; // Lower quality for smaller file sizes
} );

Sets the maximum width or height (in pixels) for optimized story images. Images larger than this are scaled down proportionally.

Default: 2048

File: public/class-wp-stories-submit-user-stories.php

add_filter( 'wp_stories_max_image_dimension', function( $dimension ) {
return 1920;
} );

wp_stories_min_image_size_for_optimization

Section titled “wp_stories_min_image_size_for_optimization”

Sets the minimum file size (in bytes) before optimization kicks in. Images smaller than this threshold are left untouched.

Default: 500000 (500 KB)

File: public/class-wp-stories-submit-user-stories.php

add_filter( 'wp_stories_min_image_size_for_optimization', function( $size ) {
return 200000; // Optimize images over 200 KB
} );

Defines the allowed image file extensions for story uploads.

Default: ['jpg', 'jpeg', 'png', 'gif']

File: includes/wp-stories-functions.php

add_filter( 'wp_stories_allowed_image_types', function( $types ) {
$types[] = 'webp';
return $types;
} );

Defines the allowed video file extensions for story uploads.

Default: ['mp4', 'mov', 'wmv', 'mpeg', '3gp']

File: includes/wp-stories-functions.php

add_filter( 'wp_stories_allowed_video_types', function( $types ) {
$types[] = 'webm';
return $types;
} );

Defines the allowed image file extensions for story collection cover images.

Default: ['png', 'gif', 'jpg', 'jpeg']

File: includes/wp-stories-functions.php

add_filter( 'wp_stories_allowed_cvr_img_types', function( $types ) {
return array( 'jpg', 'jpeg', 'png' );
} );

Sets the maximum file size (in bytes) for story collection cover images.

File: includes/wp-stories-functions.php

add_filter( 'wp_stories_allowed_cvr_img_size', function( $bytes ) {
return 2 * 1024 * 1024; // 2 MB
} );

Filters the avatar URL used for story circles.

File: includes/wp-stories-functions.php

add_filter( 'wp_stories_avatar_url', function( $avatar_url, $user_id, $size ) {
// Return a custom avatar URL
return $avatar_url;
}, 10, 3 );

Filters the display name shown on story circles.

File: includes/wp-stories-functions.php

add_filter( 'wp_stories_author_name', function( $name, $user_id ) {
// Customize the displayed author name
return $name;
}, 10, 2 );

Filters the login URL displayed to logged-out users in the story activity feed widget.

Default: wp_login_url()

File: public/widgets/class-wp-stories-activity-feed-widget.php

add_filter( 'wp_stories_login_url', function( $url ) {
return home_url( '/custom-login/' );
} );

Filters the BuddyPress template used for the stories profile tab.

Default: 'members/single/plugins'

File: public/class-wp-stories-public.php

add_filter( 'wp_stories_template_settings', function( $template ) {
return 'members/single/custom-template';
} );

Sets the number of PeepSo user stories loaded per page (pagination).

Default: 10

File: public/class-wp-stories-public.php, public/peepso-user-stories-view.php

add_filter( 'wp_stories_PeepSoUser_per_page', function( $per_page ) {
return 20;
} );

Sets the number of stories fetched for activity feeds.

Default: 50

File: includes/wp-stories-functions.php

add_filter( 'wp_story_activity_count', function( $count ) {
return 100;
} );

Sets the number of days after which expired story attachments are permanently deleted.

Default: 7

File: includes/wp-stories-functions.php

add_filter( 'wp_stories_delete_stories_attachement', function( $days ) {
return 14; // Keep attachments for 14 days
} );

Controls whether the story IDs selector in the Story Box metabox uses AJAX loading.

Default: false

File: admin/wp-stories-metabox.php

add_filter( 'wp_stories_story_ids_select_ajax', '__return_true' );

Controls whether skeleton loading placeholders are shown while stories load.

Default: true

File: public/class-wp-stories-public.php

add_filter( 'wb_stories_loaders', '__return_false' );

Sets the number of skeleton loader placeholders displayed while stories are loading.

Default: 10

File: public/class-wp-stories-public.php

add_filter( 'wb_stories_loaders_count', function( $count ) {
return 5;
} );

Fires before a new story is saved to the database. Use this to run custom validation or pre-processing.

File: public/class-wp-stories-submit-user-stories.php

add_action( 'wp_stories_before_story_submit', function() {
// Custom logic before story is saved
} );

Fires before a single story item is deleted via AJAX.

File: public/class-wp-stories-submit-user-stories.php

add_action( 'wp_stories_before_item_delete', function() {
// Log item deletion or run cleanup
} );

Fires before an entire story post is deleted via AJAX.

File: public/class-wp-stories-submit-user-stories.php

add_action( 'wp_stories_before_story_delete', function() {
// Run cleanup tasks before story removal
} );

Removed in 2.4.0. The legacy admin wrapper this fired from was replaced by the card-panel admin, and the hook went with it. It is listed here only so anyone who used it knows where it went.

Replace it with either:

  • wp_stories_admin_setting_tabs - a filter over the whole tab registry, to add a tab of your own (see below), or
  • the per-tab actions wp_stories_general_settings_options, wp_stories_display_options_options, wp_stories_user_publishing_options, wp_stories_style_options_options, wp_stories_buddypress_integration, wp_stories_peepso_integration - to append fields to a tab that already exists.

Fires after the default general settings options are rendered. Use this to add custom options to the General tab.

File: admin/inc/wp-stories-general-tab.php

add_action( 'wp_stories_general_settings_options', function( $settings ) {
// Add custom fields to General Settings
}, 10, 1 );

Fires after the default display options are rendered. Use this to add custom options to the Display Options tab.

File: admin/inc/wp-stories-display-options-tab.php

add_action( 'wp_stories_display_options_options', function( $options ) {
// Add custom fields to Display Options
}, 10, 1 );

Fires after the default styling options are rendered. Use this to add custom options to the Styling Options tab.

File: admin/inc/wp-stories-style-options-tab.php

add_action( 'wp_stories_style_options_options', function( $options ) {
// Add custom fields to Styling Options
}, 10, 1 );

Fires after the default user publishing options are rendered. Use this to add custom options to the User Publishing tab.

File: admin/inc/wp-stories-user-publishing-options-tab.php

add_action( 'wp_stories_user_publishing_options', function( $options ) {
// Add custom fields to User Publishing Options
}, 10, 1 );

Fires after the BuddyPress integration settings are rendered. Use this to add custom BuddyPress-related options.

File: admin/inc/wp-stories-integrations-tab.php

add_action( 'wp_stories_buddypress_integration', function( $integrations ) {
// Add custom BuddyPress integration settings
}, 10, 1 );

Fires after the PeepSo integration settings are rendered. Use this to add custom PeepSo-related options.

File: admin/inc/wp-stories-integrations-tab.php

add_action( 'wp_stories_peepso_integration', function( $integrations ) {
// Add custom PeepSo integration settings
}, 10, 1 );

Filter. includes/wp-stories-functions.php:189

The single access check for member stories. Every surface that serves a story - the tray, the story-media AJAX endpoint, the view recorder and the like toggle - resolves through it, so this is the one place to change who may see what.

It already enforces: post type, post_status = publish (a deleted story is moved to draft), the Story Expiry window, that the item exists and is not disabled, and the item’s per-item visibility.

// Let editors read every member story, including private ones.
add_filter( 'wp_stories_user_can_view_story_item', function ( $allowed, $post_id, $index, $user_id ) {
if ( ! $allowed && user_can( $user_id, 'edit_others_posts' ) ) {
return true;
}
return $allowed;
}, 10, 4 );
Param Type Notes
$allowed bool The decision so far.
$post_id int Story post ID.
$index int|null Item index in wb_story_items; null when checking the story itself.
$user_id int User being checked. 0 for logged-out visitors.

Returning true unconditionally re-opens the access bugs fixed in 2.4.2. Widen deliberately, and always keep a capability or ownership test in the condition.


Filter. includes/wp-stories-settings.php:181

The schema every option group is sanitized against. Register your own fields here so they get typed validation instead of the generic text fallback.

Types: flag, enum_flag, int (with min/max/default), enum, color, list_enum, list_role, list_ext.

add_filter( 'wp_stories_settings_schema', function ( $schema ) {
$schema['wp_stories_display_options']['my_addon_limit'] = array(
'type' => 'int',
'min' => 1,
'default' => 20,
);
return $schema;
} );

Fields you do not declare are still preserved on save - they are passed through a recursive text sanitizer rather than dropped - so an add-on that appends fields via the wp_stories_*_options actions keeps working without registering here.

Filter. includes/wp-stories-settings.php:339

The sanitized array, immediately before it is written. Args: $output, $input, $group.


Filter. includes/wp-stories-functions.php:1881. Default 200.

How many stories per post type the daily cleanup handles in one run. Whatever is left over is picked up by the next run.

Filter. includes/wp-stories-functions.php:1979. Args: $attachment_ids, $post_id.

The attachments deleted alongside a story. The default list contains only media whose post_parent is the story, so images picked from the Media Library are never removed.

Adding IDs here permanently deletes those attachments. Do not add media that might be used elsewhere on the site.

Filter. Historic name, kept for back-compat. It controls the retention period in days, despite reading like an attachment toggle. Since 2.4.2 the period is a normal setting (Displaying Options > Delete After Expiry), and this filter applies on top of it.


Filter. includes/wp-stories-functions.php:1621. Args: $list, $type.

The extensions considered browser-playable for image or video.

Filter. includes/wp-stories-functions.php:1638. Args: false, $type.

Return true to restrict uploads to the browser-playable list for that type.


Filter. includes/admin/class-wpst-admin.php:106. Args: $tabs.

The admin tab registry. Each entry is keyed by tab slug with label, icon (dashicon class) and group (main, settings or account).

add_filter( 'wp_stories_admin_setting_tabs', function ( $tabs ) {
$tabs['my-addon'] = array(
'label' => __( 'My Add-on', 'my-addon' ),
'icon' => 'dashicons-admin-generic',
'group' => 'settings',
);
return $tabs;
} );

Filter. includes/admin/class-wpst-plugin-installer.php:207. Args: $catalog.

The Discover tab’s install catalog, keyed by plugin slug.

The catalog is an allow-list, and it is the reason the install endpoint cannot be pointed at arbitrary code. Anything you add here becomes installable by an administrator in one click, and its package must resolve on the store host. Do not add entries from untrusted input.

Filter. edd-license/edd-plugin-license.php:120. Default true.

sslverify on the EDD licence API call.


Filter. public/peepso-user-stories-view.php:157 and public/class-wp-stories-public.php:1590. Default 10.

Viewers loaded per page in the PeepSo story-viewer list.