Skip to content

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
} );

The default image extensions offered and accepted for story uploads, before the owner’s Allowed File Types setting is saved.

Default: the image types WordPress accepts, limited to browser-playable ones: jpg, jpeg, jpe, png, gif, webp, avif. SVG/SVGZ are always removed after this filter.

File: includes/wp-stories-functions.php (get_wp_stories_default_allowed_image_types())

add_filter( 'wp_stories_allowed_image_types', function( $types ) {
return array_values( array_diff( $types, array( 'gif' ) ) ); // No GIFs by default.
} );

The default video extensions offered and accepted for story uploads, before the owner’s Allowed File Types setting is saved.

Default: the video types WordPress accepts, limited to browser-playable ones: mp4, m4v, webm, ogv, mov, qt. Formats such as WMV, MPEG or 3GP are left out because most browsers cannot play them; use wp_stories_restrict_to_web_playable or the settings checkboxes to allow them.

File: includes/wp-stories-functions.php (get_wp_stories_default_allowed_video_types())

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 author name shown on story circles and in the viewer header.

Since 2.5.0 the default is the name members see elsewhere on the site: the PeepSo full name, else the BuddyPress display name, else the WordPress display name. Earlier versions returned the login name. Messaging links use the member’s URL slug, not this value, so changing the name never breaks them.

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';
} );

Rows per page in the PeepSo “Seen by” list (the viewers of one story item); Load more fetches the next page. The BuddyPress list uses wp_stories_viewers_per_page.

Default: 10

File: includes/wp-stories-functions.php (wp_stories_viewer_list_html())

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;
} );

How many days an expired story is kept before scheduled deletion removes it (and, if Delete Story Attachments is on, its uploaded media).

Default: the Delete After Expiry setting on the Displaying Options tab, or 7 when it is not set.

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 Collection 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: includes/admin/views/settings-general.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: includes/admin/views/settings-display.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: includes/admin/views/settings-style.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: includes/admin/views/settings-user-publishing.php

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

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

File: includes/admin/views/settings-integrations.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: includes/admin/views/settings-integrations.php

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

Filter. includes/wp-stories-functions.php

The access check used by the story AJAX endpoints: the story-media endpoint, the view recorder and the like toggle. The stories row itself is built from wp_stories_visibility_allows(), which this filter does not change, so a rule added here controls what those endpoints serve, not which circles appear.

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

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

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


Filter. includes/wp-stories-functions.php. 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. 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. Args: $list, $type.

The extensions considered browser-playable for image or video. Since 2.5.0 this is also the default upload allowlist.

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

Since 2.5.0 the default is true: until the owner saves Allowed File Types, members can only upload formats browsers can play (JPG, PNG, GIF, WebP, AVIF, MP4, M4V, MOV, WebM, OGV). Formats like AVI, WMV, MKV or HEIC uploaded fine before but never played in the viewer. Return false to default to every type WordPress accepts. Owners can also widen the list on the User Publishing tab, which always offers every WordPress-accepted type.


Filter. includes/admin/class-wpst-admin.php. 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. 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. Default true.

sslverify on the EDD licence API call.


Filter. includes/wp-stories-functions.php (wp_stories_viewer_list_html()). Default 10.

Rows per page in the PeepSo “Seen by” list before “Load more”.

Filter. includes/wp-stories-functions.php (wp_stories_viewer_list_html()). Args: $per_page. Default 20.

How many people the BuddyPress “Seen by” list shows before “Load more”. Both lists show the most recent viewers first.

Filter. includes/wp-stories-functions.php. Args: $show, $post_type. Default false.

Whether wb-story, wb-story-box or wb-user-story is exposed in the WordPress REST API. Off by default: the plugin itself uses AJAX only, and the core REST collections do not apply story visibility, so private and friends-only stories would be listed. Return true only if you add your own permission checks.

add_filter( 'wp_stories_show_in_rest', function ( $show, $post_type ) {
return 'wb-story-box' === $post_type; // Admin story boxes only.
}, 10, 2 );

Filter. includes/wp-stories-functions.php. Args: $allowed.

Whether the current member may post stories. Default: logged in and in one of the roles ticked under User Publishing > Authorized User Roles (all roles when none are ticked). The same rule decides the “Add story” buttons, whether the submit form is printed, and whether the upload request is accepted (403 otherwise).

Filter. public/class-wp-stories-public.php. Args: $lazy. Default true.

Pages that show stories load only the small viewer script. For members who can post, the posting editor (FilePond, image editor, form script) is printed into a <template id="wpst-editor-assets"> and loaded on their first “Add story” tap. Return false to load it with the page instead, for example if a script optimizer moves or combines the template’s contents.

add_filter( 'wp_stories_lazy_editor', '__return_false' );

Action. public/class-wp-stories-submit-user-stories.php.

Fires when a story submit form is printed. WP Stories uses it to queue the posting editor for that page.

Filter. includes/wp-stories-functions.php. Args: $limit. Default 50.

How many stories a site-wide tray fetches when no member is given (the public stories widget, the user-stories shortcodes without an id), most recent first. Matches the activity tray’s wp_story_activity_count. One member’s stories are not capped here; the per-member story limit already bounds them.

Set these in your theme or in Appearance > Customize > Additional CSS. Each one falls back to the plugin default when it is not set.

Size of the Instagram-style story circles, the “Add story” circle and their loading placeholders. Default min(18vw, 90px). The Circle Size setting sets this for you. Set it yourself for an exact size:

.stories.snapgram,
.wp-stories-user-stories:has(> .stories.snapgram) {
--wpst-circle-size: min(20vw, 100px);
}

Font size of the names under the story circles. Default 12px.

Color of the heart shown when a member double-taps a story to like it. Default #fff.