Standalone mode exposes a WooCommerce style layer of actions and filters so themes and add-ons can extend, reorder, restyle and react to MLSImport without editing the plugin.
Every section builder, whether shortcode, Gutenberg block, Elementor widget or PHP template, routes through one dispatcher, and every search goes through one query executor. A hook added at those points applies on every surface at once.
Before you start
- Standalone mode is active. These hooks belong to the standalone front end and do not fire when the plugin is running against WpResidence, WpEstate, Houzez or Real Homes.
- Your code lives in a child theme’s
functions.phpor a small plugin of your own, never in MLSImport itself. - The canonical machine readable list is
includes/standalone/mlsimport-hooks.phpin the plugin. This page is the signature reference.
Conventions
- Every hook is prefixed
mlsimport_. - Filters receive the subject as the first argument and must return it, modified or not. Context arguments follow it.
- Actions receive the most relevant subject first and return nothing.
- Section action slots are buffered. You
echoinside them and the dispatcher captures that output into the section’s returned string, so nothing leaks out of order. - The two SQL affecting filters are advanced. Their output is re validated for placeholder safety after your callback runs.
- No hook may set or clear the editorial
mlsimport_labeltaxonomy.
Rendering a property
mlsimport_property_sectionsfilter( array $sections ), add, remove or reorder the section manifest globally.mlsimport_section_argsfilter( array $args, string $slug, int $id ).mlsimport_before_sectionandmlsimport_after_sectionactions( string $slug, int $id, array $args ), buffered.mlsimport_before_section_{$slug}andmlsimport_after_section_{$slug}actions( int $id, array $args ), buffered.mlsimport_section_{$slug}_htmlfilter( string $html, int $id, array $args ), replace one section’s markup.mlsimport_section_htmlfilter( string $html, string $slug, int $id, array $args ).mlsimport_property_datafilter( array $vm, int $id ), the view model every section reads.mlsimport_property_field_sectionfilter( string $section, string $field ), move a RESO field between detail sections.mlsimport_format_pricefilter( string $string, mixed $value ).
Rendering a listings grid
mlsimport_render_argsfilter( array $args ).mlsimport_prepared_listingsfilter( array $payload, array $args ).mlsimport_before_listingsaction( array $args, int $total ).mlsimport_before_search_formandmlsimport_after_search_formactions( array $args ).mlsimport_before_resultsandmlsimport_after_resultsactions( array $args, int $total ).mlsimport_no_results_messagefilter( string $html, array $args ).mlsimport_before_listing_cardandmlsimport_after_listing_cardactions( WP_Post $post, object|null $row ), buffered.mlsimport_listing_card_htmlfilter( string $html, WP_Post $post, object|null $row ).mlsimport_card_templatefilter( string $name, string $style ), which card file a grid renders.mlsimport_card_viewfilter( array $view, WP_Post $post, object|null $row ), plus the in-card action slotsmlsimport_card_before,mlsimport_card_after_media,mlsimport_card_body_start,mlsimport_card_body_endandmlsimport_card_after.mlsimport_listings_visible_fieldsfilter( array|null $keys, array $args ), which search fields a grid’s bar shows. Null means every field.mlsimport_map_markersfilter( array $markers, array $args ).
Query
mlsimport_listings_query_paramsfilter( array $params ), the structured way to change a search.mlsimport_listings_resultsfilter( array $result, array $params ), holding rows and total.mlsimport_listings_feature_ttidsfilter( int[] $ttids, array $params ).mlsimport_listings_per_pagefilter( int $limit, array $params ).mlsimport_listings_query_wherefilter( array $where, array $params ), advanced.mlsimport_listings_orderfilter( string $order, array $params ), advanced and re validated.
Reach for mlsimport_listings_query_params before either of the two SQL filters. It expresses the same intent in a form the query builder understands, so it keeps working when the underlying SQL changes, which the raw filters do not.
Templates and assets
mlsimport_template_namefilter( string $name )andmlsimport_template_subdirfilter( string $subdir ).mlsimport_locate_templatefilter( string $path, string $name, string $subdir ), the full override of a resolved path.mlsimport_template_includefilter( string $located, string $template ).mlsimport_single_property_sectionsfilter( string[] $slugs, int $id ), the single page section order.mlsimport_before_single_propertyandmlsimport_after_single_propertyactions( int $id ).mlsimport_before_archiveandmlsimport_after_archiveactions( array $args ).mlsimport_standalone_stylesfilter( bool $enqueue ), disable the bundled stylesheet.mlsimport_map_tile_urlfilter( string $url ), the tile source every map draws from. The maps are Leaflet, so this is where a different tile provider is swapped in.mlsimport_property_schemafilter( array $schema, array $vm ), the JSON-LD graph printed into a single property’s head. Return an empty array to emit nothing.
Import lifecycle
mlsimport_before_save_propertyandmlsimport_after_save_propertyactions( int $property_id, array $property ).mlsimport_property_skip_writefilter( bool $skip, int $property_id, string $incoming_mod, array $property ).mlsimport_property_meta_valuefilter( mixed $value, string $meta_key, int $property_id, array $property ).mlsimport_property_feature_labelfilter( string $label, string $reso_field, mixed $value ).mlsimport_property_post_contentfilter( string $content, array $property ).mlsimport_reso_targets_forfilter( array $targets, string $field ), where a RESO field is written.mlsimport_listings_rowfilter( array $row, int $post_id, string $listing_key )andmlsimport_after_listings_row_upsertaction( int $post_id, string $listing_key, array $row ).mlsimport_before_delete_listing_rowandmlsimport_after_delete_listing_rowactions( int $post_id ).mlsimport_reindex_propertyaction( int $post_id )andmlsimport_after_reindexaction( int $count ).
Leads and registration
mlsimport_property_lead_fieldsfilter( string $fields, string $variant, int $id ).mlsimport_property_lead_validationfilter( array $errors, array $input, int $property_id ).mlsimport_property_lead_subject,mlsimport_property_lead_email_bodyandmlsimport_property_lead_recipientfilters.mlsimport_property_lead_submittedaction( array $data, int $property_id )andmlsimport_property_lead_mail_sentaction( bool $sent, array $data, int $property_id ).mlsimport_property_post_type_argsandmlsimport_agent_post_type_argsfilters( array $args ).mlsimport_taxonomiesfilter( array $list )andmlsimport_taxonomy_argsfilter( array $args, string $taxonomy ).mlsimport_registered_cptsaction, fired once registration is complete.
Live mode
Live mode answers reads straight from the MLS through pre filter seams. A callback that returns something other than null short circuits the stored path, and every live callback guards on the one gate below. Live mode itself is not switched on in the current release, so these seams exist and stay inert until it ships.
mlsimport_live_mode_activefilter( bool $active ), the gate.mlsimport_live_cache_ttlfilter( int $ttl )in seconds, andmlsimport_live_url_basefilter( string $base ).mlsimport_property_data_prefilter( array|null $pre, int $id ).mlsimport_map_payload_prefilter( array|null $pre, array $args, int $zoom )andmlsimport_map_bounds_prefilter( array|null $pre, array $params ).mlsimport_search_field_optionsfilter( array $pairs, array $def ).mlsimport_page_block_list_by_id_prefilter( string|null $pre, array $args ).
Worked examples
Add a call to action under the price, on every surface at once:
add_action( 'mlsimport_after_section_price', function ( $id, $args ) {
echo '<a class="cta" href="/mortgage?listing=' . esc_attr( $id ) . '">Estimate your payment</a>';
}, 10, 2 );
Remove a section everywhere, then reorder the single page specifically:
add_filter( 'mlsimport_property_sections', function ( $sections ) {
unset( $sections['similar'] );
return $sections;
} );
add_filter( 'mlsimport_single_property_sections', function ( $slugs, $id ) {
array_unshift( $slugs, 'video' );
return array_values( array_unique( $slugs ) );
}, 10, 2 );
Route a RESO field somewhere else on import, and turn off the bundled stylesheet:
add_filter( 'mlsimport_reso_targets_for', function ( $targets, $field ) {
if ( 'GreenEnergyEfficient' === $field ) {
return array( 'tax:mlsimport_feature' );
}
return $targets;
}, 10, 2 );
add_filter( 'mlsimport_standalone_styles', '__return_false' );
Prefer a hook to a copied template whenever both would work. A hook keeps receiving plugin updates, and an overridden template stops the day its bundled version changes, which is the same trade off WooCommerce theme developers live with.
Frequently asked questions
Do these hooks fire on WpResidence or Houzez?
No. They belong to the standalone front end. On a supported theme the property pages are the theme’s, and its own hooks apply.
How do I see the current list rather than this page?
Read includes/standalone/mlsimport-hooks.php in the plugin. It is the canonical list, and it is kept accurate because the code is generated from the same conventions.
Can I register a section of my own?
Yes. Add it to the manifest through mlsimport_property_sections and return its markup from mlsimport_section_{$slug}_html.
Why does my echo inside a section hook appear in the right place rather than at the top of the page?
Because section slots are buffered. The dispatcher captures your output and folds it into the section’s returned string, which is what preserves the order on every surface.
Related
- Template overrides in standalone mode
- Sending property leads to a CRM
- Standalone mode: run MLSImport on any theme
- Rebuilding the search index with WP-CLI
- What Live Mode is and when to use it
Open a ticket with the behaviour you are trying to change if none of the hooks above reaches it.