Templates and Overrides
BuddyPress Member Blog overviewFree + ProGet BuddyPress Member Blog →
A theme can replace the plugin’s page templates without editing the plugin. Copy the plugin’s file into your theme at the path shown below, keep the filename, and edit your copy. The override survives plugin updates. Every lookup checks the child theme first, then the parent theme, then falls back to the plugin’s own copy.
Written against version 4.3.0.
What you can override
Section titled “What you can override”| Plugin file | Theme path | Rendered on |
|---|---|---|
templates/edit.php |
yourtheme/bp-member-blog/edit.php |
The post composer (new post and edit), on every platform. |
templates/posts.php |
yourtheme/bp-member-blog/posts.php |
A member’s Published, Drafts or Pending list, on the dashboard and on every profile. |
templates/peepso/edit.php |
yourtheme/buddypress-member-blog/peepso/edit.php |
The PeepSo wrapper around the composer. |
templates/writer-profile.php |
yourtheme/buddypress-member-blog/writer-profile.php |
The writer profile at /author/{name}/. |
templates/topic-hub.php |
yourtheme/buddypress-member-blog/topic-hub.php |
Topic hubs at /category/{slug}/ and /tag/{slug}/. |
| (none shipped) | yourtheme/buddypress-member-blog/emails/{id}.php |
The body of one email. See Emails. |
Pro: templates/series-landing-page.php |
yourtheme/buddypress-member-blog-pro/series-landing-page.php |
(Pro) A series landing page. |
Other Pro templates (group posts, publishing calendar, scheduled posts, analytics, co-author selector, invitation list) are not theme-overridable; change them through their hooks.
Two directories
Section titled “Two directories”The composer and the post list are loaded by bp_member_blog_load_template() from the
bp-member-blog/ directory of your theme. Change that directory name with a filter:
add_filter( 'bp_member_blog_template_dir', function () { return 'templates/member-blog'; // yourtheme/templates/member-blog/posts.php} );Everything else is looked up under buddypress-member-blog/ in your theme, and that name is fixed.
Changed in 4.3.0
Section titled “Changed in 4.3.0”- One
posts.phpnow renders all three lists (Published, Drafts, Pending) on every platform. The separatedraft-posts.php,pending-posts.phpand the PeepSo copies of all three are gone. An old override of one of those files is no longer loaded; move your changes intoposts.php. - PeepSo uses the same composer as everyone else.
peepso/edit.phponly wrapsedit.phpin PeepSo’s container, so overridebp-member-blog/edit.phpto change the form itself. - Template parts (
parts/post-card.php,parts/empty-state.php) and thebpmb_get_template_part()/bpmb_template_part_pathpair no longer exist. Cards are drawn bybpmb_post_cards(); restyle them with CSS tokens (see Styling).
Overriding the post list
Section titled “Overriding the post list”The shipped posts.php is one call:
<?phpdefined( 'ABSPATH' ) || exit;
$bpmb_list = bpmb_member_home()->list_context();
echo bpmb_member_home()->post_list( (int) $bpmb_list['user_id'], (string) $bpmb_list['tab'], (string) $bpmb_list['mode'] );list_context() gives you user_id (the home owner), tab (posts, drafts or pending) and
mode. post_list() returns the toolbar, the cards, the pager and the empty state, with the query,
co-authored posts and permissions already applied. An override can wrap or reorder that output
without rebuilding any of it:
<?phpdefined( 'ABSPATH' ) || exit;
$bpmb_list = bpmb_member_home()->list_context();
if ( 'posts' === $bpmb_list['tab'] ) { echo '<p class="my-list-intro">' . esc_html__( 'Everything this writer has published.', 'my-theme' ) . '</p>';}
echo bpmb_member_home()->post_list( (int) $bpmb_list['user_id'], (string) $bpmb_list['tab'], (string) $bpmb_list['mode'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- renderer escapes.Hooks around the list, which usually make an override unnecessary:
| Hook | Type | Arguments |
|---|---|---|
bp_member_blog_before_posts |
action | string $status, int $user_id |
bpmb_posts_toolbar |
action | string $post_status (publish, draft or pending) |
bp_member_blog_posts_per_page |
filter | int $per_page |
To add or remove whole tabs on the member home, use bpmb_member_home_tabs; see
Extending.
Overriding the composer
Section titled “Overriding the composer”edit.php is long. Before copying it, check whether a hook does the job; an override has to be
merged by hand every time the plugin changes the form.
Every hook below receives int $post_id (0 for a new post):
| Hook | Where |
|---|---|
bp_post_before_title, bp_post_after_title |
Around the title. |
bp_post_before_content, bp_post_after_content |
Around the editor. |
bp_post_before_category, bp_member_blog_before_category, bp_post_after_category |
Around the categories. |
bpmb_composer_panel_collaboration |
The Collaboration panel of the settings sidebar (Pro adds co-authors here). |
bp_post_before_tag, bp_post_after_tag |
Around the tags. |
bp_post_before_featured_image, bp_post_after_featured_image |
Around the featured image. |
bpmb_composer_panel_details |
The Details panel of the settings sidebar. |
bp_post_before_seo, bp_post_after_seo |
Around the SEO fields. |
bp_post_after_publishing_options |
After the publishing options. |
bpmb_composer_panel_publishing |
The Publishing panel of the settings sidebar. |
bp_post_before_submit_button |
Before the action buttons. |
The filter bp_member_blog_post_content (string $content) changes the content loaded into the
editor when a post is opened.
Add a field without touching edit.php:
add_action( 'bp_post_after_content', function ( $post_id ) { $value = $post_id ? get_post_meta( $post_id, '_my_subtitle', true ) : ''; ?> <div class="bp-member-blog-field"> <label for="my-subtitle"><?php esc_html_e( 'Subtitle', 'my-plugin' ); ?></label> <input type="text" id="my-subtitle" name="my_subtitle" value="<?php echo esc_attr( $value ); ?>" /> </div> <?php} );Saving the value is your code’s job, for example on save_post.
The archive takeovers
Section titled “The archive takeovers”The plugin can take over two core archive URLs. Both are switches on the Features tab of the Member Blog settings (Writer profiles and Topic hubs), and both have a filter for code.
/author/{name}/ becomes the writer profile
Section titled “/author/{name}/ becomes the writer profile”src/Frontend/AuthorArchive.php swaps in writer-profile.php on template_include (priority 20).
With BuddyPress active, the member profile stays canonical: the writer profile prints a
<link rel="canonical"> pointing at it. There is no redirect.
/** * @param bool $enabled Defaults to the Writer profiles switch. * @param int $user_id The writer being viewed. */add_filter( 'bpmb_takeover_author_archive', function ( $enabled, $user_id ) { return $enabled && user_can( $user_id, 'edit_posts' );}, 10, 2 );Return false and the theme’s author.php renders again. bpmb_writer_url() keeps pointing at the
same URL either way.
| Hook | Type | Arguments |
|---|---|---|
bpmb_writer_profile_per_page |
filter | int $per_page (default 12), int $user_id |
bpmb_before_writer_profile |
action | int $user_id |
bpmb_after_writer_profile |
action | int $user_id |
Paging uses the main query, so /author/jane/page/2/ works.
Category and tag archives become topic hubs
Section titled “Category and tag archives become topic hubs”src/Frontend/TermArchive.php swaps in topic-hub.php for the taxonomies in
bpmb_topic_hub_taxonomies (default category and post_tag).
/** * Keep hubs for categories and hand tag archives back to the theme. * * @param bool $enabled Defaults to the Topic hubs switch. * @param \WP_Term $term The term being viewed. */add_filter( 'bpmb_takeover_term_archive', function ( $enabled, $term ) { return $enabled && 'category' === $term->taxonomy;}, 10, 2 );| Hook | Type | Arguments |
|---|---|---|
bpmb_topic_hub_per_page |
filter | int $per_page (default 12), \WP_Term $term |
bpmb_before_topic_hub |
action | \WP_Term $term |
bpmb_after_topic_hub |
action | \WP_Term $term |
Shortcode empty states
Section titled “Shortcode empty states”When a listing shortcode has nothing to show, it prints one designed empty state instead of a blank gap:
<div class="bpmb-empty bpmb-shortcode-empty-state"> <span class="bpmb-empty__icon">...</span> <h3 class="bpmb-empty__title">...</h3> <p class="bpmb-empty__text">...</p> <div class="bpmb-empty__actions">...</div></div>Target .bpmb-empty.bpmb-shortcode-empty-state to style shortcode empty states without touching
other empty states. When the list is empty because of how the shortcode was set up (for example, no
topic given or nothing featured yet), users with manage_options also see a
<p class="bpmb-admin-hint"> note under it explaining the fix. Visitors never see that note.
Rules for an override
Section titled “Rules for an override”- Draw cards through the plugin. Use
bpmb_post_cards()orbpmb_member_home()->post_list()rather than your own card markup, so your list matches every other list and keeps the right action buttons. - Ask, do not re-check. To know whether the viewer may edit a post, call
bpmb_access()->can_edit( $post_id ). - Link writers with
bpmb_writer_url( $user_id ), notget_author_posts_url(). - Keep the class names. Button geometry is enforced on
.bpmb-follow-btn,.bpmb-clap-btnand.bpmb-bookmark-btn. Restyle with CSS tokens instead. - Do not query in a template. Ask the service that owns the data. For example, Pro’s group
listing uses
Buddypress_Member_Blog_Pro_Groups::group_posts_query( $group_id ), which applies the member scope, thebpmb_group_posts_query_argsfilter and cache priming.
The full list of hooks, with the file and line each is fired from, is in the Hook Reference.

