Goal
Inject your own content into the app’s home screen from a sibling plugin or a
site snippet — without editing core. The home screen is rendered from an ordered
array called the home-screen stack (homescreen_stack). The mam_hss_*
filter family lets you add sections at specific positions: pinned above the
scrollable content, directly under the nav header, or appended to the end of the
stack as a call-to-action.
hss stands for home screen stack.
By the end you will know which filter fires at which position, what shape each
one expects you to return, and how to add a banner and an end-of-stack CTA that
the iOS and Android apps will render.
How the stack is built
The stack is assembled by
mam_manage_home_screen_stack_content::create_home_screen_stack() in
mam-main/includes/app-settings/manage-homescreen-stack-content.php. It runs
during phone-data build (the payload the app downloads), so every filter below
fires on a hot path — keep your callbacks cheap.
The build proceeds in this order, and the filters fire in this sequence:
- A
nav_headersection is pushed onto the stack. mam_hss_stack_after_nav_header— runs on the whole stack array, right
after the nav header is added.mam_hss_pin_to_top— returns the list of section types that should be
flagged as pinned (see the important note below).- Each saved stack section is appended in admin order. As each one is added,
mam_hss_stack_itemfires for that single section. mam_hss_end_of_stack— runs on the whole stack after all saved sections
are appended. This is where you add trailing content.- Sections whose
typeis in the pin-to-top list get'pin_to_top' => 'yes'. mam_hss_stack_final— last pass over the whole stack before it ships.
Two more fire conditionally:
mam_hss_stack_after_bottom_tabbar— fires inside the bottom-tab-bar
section handler, only when the stack contains abottom_tabbarsection.mam_hss_single_listing— fires only if at least one callback is
registered, when asingle_listingsection is encountered.
Each section is a plain associative array with at least a type key (for
example array( 'type' => 'text', 'title' => '...' )). The app keys its
renderers off type, so the values you return are part of the frozen mobile
contract — use the same shapes the core handlers produce.
Prerequisites
- A place to register a filter: a sibling MAM plugin, an mu-plugin, or a
snippet that runs on the website (these filters fire server-side during
phone-data build, not in the app). mam-mainactive (it owns the stack build and the filters).- Familiarity with the section
typestrings the app understands — for example
text,image,filter_bar,geo_filters,tag_cloud,map,
bottom_tabbar. The built-in handlers in
manage-homescreen-stack-content.phpare the reference for the exact keys
each type accepts. - Test on a real device or simulator pointed at your environment. The stack is
visual; position changes are easy to get wrong on inspection alone.
Steps
1. Pick the position, then pick the filter
| You want to… | Use | Shape you receive / return |
|---|---|---|
| Add content just under the nav header | mam_hss_stack_after_nav_header |
the whole stack array; push your section(s), return the array |
| Mark sections as pinned above the scroll | mam_hss_pin_to_top |
an array of section type strings to merge into the pin list |
| Modify one section as it is added | mam_hss_stack_item |
($section, $index); return the (possibly modified) single section |
| Append CTAs / banners to the end | mam_hss_end_of_stack |
the whole stack array; append, return the array |
| Floating content above the bottom tab bar | mam_hss_stack_after_bottom_tabbar |
the whole stack array (only when a tab-bar section exists) |
| Last-chance edit of the entire stack | mam_hss_stack_final |
the whole stack array |
Render a custom single_listing section |
mam_hss_single_listing |
($item, $index); return a section array (or empty to skip) |
Most injection work uses mam_hss_stack_after_nav_header (top) or
mam_hss_end_of_stack (bottom).
2. Add a post-nav banner
mam_hss_stack_after_nav_header receives the stack array immediately after the
nav_header section, so anything you push here appears at the very top of the
scrollable content.
add_filter( 'mam_hss_stack_after_nav_header', function ( array $stack ): array {
$banner = my_get_active_home_banner(); // your own lookup
if ( ! $banner ) {
return $stack;
}
$stack[] = array(
'type' => 'image',
'image' => $banner['image_url'],
'link' => $banner['target_url'],
);
return $stack;
} );
Match the keys an image section actually uses in your build (see
make_stack_image() in the stack handler) so the app renders it correctly. When
unsure, configure one image section in the admin and inspect the resulting
homescreen_stack entry, then mirror that shape.
3. Append an end-of-stack CTA
mam_hss_end_of_stack runs after every saved section has been appended, so it
is the right place for a trailing call-to-action.
add_filter( 'mam_hss_end_of_stack', function ( array $stack ): array {
$stack[] = array(
'type' => 'text',
'title' => 'Become a member — members get exclusive deals',
'align' => 'center',
);
return $stack;
} );
Use a section type the app already renders. If you need a richer button or
card, model it on the keys an existing section of that type produces rather than
inventing new ones.
4. Understand pinning (it is not what it looks like)
mam_hss_pin_to_top does not take section arrays. Its return value is
merged into a list of section type strings:
$pin_to_top = array_merge(
array( 'filter_bar', 'listing_search', 'geo_filters', 'tag_cloud' ),
apply_filters( 'mam_hss_pin_to_top', array() )
);
After the stack is built, any section whose type is in that list gets
'pin_to_top' => 'yes' set on it, which keeps it fixed above the scrollable
content. So to pin a section type:
add_filter( 'mam_hss_pin_to_top', function ( array $types ): array {
$types[] = 'my_custom_section_type';
return $types;
} );
To actually add a pinned visual element, you add the section in a positional
filter (step 2 or 3) and include its type here so it gets the pin flag.
5. Modify sections in flight (optional)
mam_hss_stack_item fires once per saved section as it is appended, receiving
($section, $index). Use it to enrich an existing section — never to push new
ones (that is what the positional filters are for):
add_filter( 'mam_hss_stack_item', function ( array $section, int $index ): array {
if ( 'text' === ( $section['type'] ?? '' ) ) {
$section['bold'] = 'bold';
}
return $section;
}, 10, 2 );
mam_hss_single_listing is the per-listing equivalent and only fires when a
callback is registered; it is meant for enriching a single listing entry, not
for filtering the listings array.
6. Return the right thing, every time
These are filters, so always return a value:
- Whole-stack filters (
after_nav_header,end_of_stack,stack_final,
after_bottom_tabbar) must return the full array, even on early exit. - Per-item filters (
stack_item,single_listing) must return the single
section array. pin_to_topmust return the array of type strings.
Returning null or a wrong shape will drop sections or break the stack.
7. Verify on device
Rebuild the app’s phone data (open the app fresh or trigger a refresh) and
confirm: the banner sits where you expect relative to the nav header, the CTA is
last, and pinned sections stay put while the rest scrolls. Because all of these
fire during phone-data build, a slow callback shows up as a slow first paint —
profile if you added any non-trivial lookups.
Notes and gotchas
- Position semantics matter.
mam_hss_stack_after_nav_headercontent is part
of the scrollable stack, just below the nav header. Pinning (via
mam_hss_pin_to_top) is a separate mechanism that fixes a section above the
scroll. They are easy to conflate. pin_to_topis a type list, not an item list. This is the most common
mistake — see step 4.- Frozen contract. Section
typevalues and their keys are consumed by
deployed apps. Reuse existing shapes; do not rename keys. - Hot path. Every filter here fires during phone-data build. Cache or guard
anything expensive. - A real example to copy.
mam-mainitself subscribes to
mam_hss_stack_after_nav_headerto inject invite buttons
(mam_app_user_manager::add_invite_buttons) — a good reference for the
whole-stack-array pattern.
Related
- Hooks: home-screen stack (`mamhss`)* — the per-hook reference table.
- Content class: Home for tab bar — the tab-bar shortcut that navigates to the
stack (distinct from the stack itself). - Phone data pipeline phases — where the stack build sits in the larger
payload assembly. - The
mam_main_populate_hss_categories/mam_main_final_home_catsfilters
participate in the relatedhome_catsbuild; see the hook reference for how
they differ from themam_hss_*stack filters.
