Add an apartment listing and property search to WordPress

Published Updated 3 min read

Build a website for an apartment management company and the obvious move is a property management plugin. RentPress and RentFetch are the ones people try first, and both are built for operators who need lease management, resident portals and maintenance requests. If the site needs to show twenty properties, filter them by city and link each to a contact form, that is a commercial system loaded to solve a display problem.

Then come the conflicts. Specialist property plugins register their own templates, enqueue their own styles and assume they own certain URLs. On a site where Astra or Blocksy with Elementor Pro already handles layout, the result is often visual breakage, duplicate queries or a settings screen fighting the theme, and the 'more compatible' alternatives tend to be other large plugins with the same assumptions.

For a small management company, a thin plugin does the job: a Property post type, the fields a listing needs, and a shortcode that outputs a filterable card grid. The layout stays in Elementor. No templates you must use, no styles on every page, no admin screens duplicating wp-admin.

A display problem, not a property platform

RentPress and its peers solve problems like syncing vacancies from an external property management system, taking lease applications, routing maintenance requests and running resident logins. Those are real needs for an operator with hundreds of units and an office team.

A company with a dozen apartment complexes using WordPress as its public marketing site needs three things: its properties with photos and details, a city filter, and a way to enquire about each one. That is the same job a blog with a custom post type has done for fifteen years.

So this build registers no page templates, does not override the_content, and adds no admin menus beyond its Settings page. It outputs one shortcode and stays out of the theme's way.

A Property post type and a City taxonomy

Properties get an archive at /properties/. Each has a title, a featured image and an optional description in the main editor, plus a meta box for Address, State, Bedrooms (text, so '1-3 BR' works), Rent range (text, so '$1,200-$2,400/mo' works) and Units available (a number).

City is its own taxonomy, so a property can sit in one or more cities and each city gets a clean archive at /properties/city/austin/ that your theme renders like any other archive, with no custom template. Both the post type and the taxonomy are registered with show_in_rest true, so they appear in the block editor sidebar and the REST API.

The shortcode: filtering by city or state

[properties] on any page, or in Elementor's Shortcode widget, outputs a responsive card grid: name, city, address, bedrooms, rent range, excerpt and an optional enquiry button. It takes city='austin' (matched against the taxonomy slug), state='TX' (a case-insensitive match on the State field) and limit, which defaults to 12.

The enquiry button goes wherever you point it under Settings > Property Listings Settings: a mailto link, a contact page or a WhatsApp link with a pre-filled message. Leave it empty and the button disappears. That keeps the plugin independent of whatever form plugin you already run.

The card styles are a small block of CSS scoped to .spl-card: cards wrap at a 280px minimum width, white with a border and padding, stacking into one column under 600px. The container sets no background, so it takes on whatever the theme puts behind it, and nothing leaks onto other pages.

Leases, maps and hundreds of listings

Availability tracking, online lease applications and a resident portal belong to a property management platform with a WordPress integration, not a WordPress plugin pretending to be one.

There is no map and no radius search, only a text list with city and state filters. A follow-up message can add a second shortcode that loads Leaflet.js and plots the address fields as pins.

If the list grows to several hundred properties, reconsider a single shortcode loop. At that size, a search index or a server-side filter with pagination and faceted queries serves visitors better than one WP_Query pulling everything on page load.

What you get

Included

  • Registers a Property custom post type with fields for address, city, state, bedrooms, rent range, and units available
  • A City taxonomy with clean archive pages at /properties/city/{slug}/ that your theme renders without a custom template
  • A [properties] shortcode with optional city= and state= parameters and a configurable limit
  • Admin list columns showing City, Bedrooms, and Rent Range so you can scan the full inventory at a glance
  • A Settings field to configure the enquiry button URL shown on each property card

Not included

  • Map-based property search or plotting address fields as map pins
  • Availability tracking, online lease applications, or tenant portals
  • MLS or IDX data feed integration
  • Radius search or price-range filter sliders beyond city and state text matching
  • Multiple enquiry actions or per-property contact routing to different team members

RentPress does the things on the right. This build covers the part most sites use.

Questions

Can I import properties from a spreadsheet?

Not in this build. For a one-off import, WP All Import reads CSV files and can map columns to custom post meta, including the fields this plugin registers.

How do I add an enquiry form to an individual property page?

Put your existing form's shortcode in the property's content in the editor. WPForms, Contact Form 7 or any form plugin that works on a normal post works on a property post; the plugin has no form of its own and sits alongside yours.

Build it in Steem

The button opens the builder with this guide's prompt filled in, so you can change anything before it runs. Nothing is built, and nothing is charged, until you send it.

Build this pluginAbout 55 credits. The free plan includes enough for one.
Read the prompt (411 words)

Build a WordPress plugin called Simple Property Listings. Plugin Name: Simple Property Listings. Description: A custom post type and shortcode for apartment and property directories. Single PHP file. Register a custom post type called property with label Properties, supports title editor thumbnail excerpt, menu_icon dashicons-building, has_archive true, rewrite slug properties, show_in_rest true. Register a custom taxonomy called property_city tied to the property post type with label City, hierarchical false, rewrite slug properties/city, show_in_rest true. Add a meta box titled Property Details to the property post type with these fields: Address (text, name _spl_address), State (text, name _spl_state), Bedrooms (text, placeholder 1-3 BR, name _spl_bedrooms), Rent range (text, placeholder $1,200-$2,400/mo, name _spl_rent_range), Units available (number, name _spl_units). Include a hidden nonce field spl_meta_nonce. On save_post, verify the nonce with wp_verify_nonce, check current_user_can edit_post, then update_post_meta for each field. Sanitize text fields with sanitize_text_field and units with absint. Add admin columns to the property post list screen: City (output the first property_city term name or a dash), Bedrooms (from _spl_bedrooms meta), Rent Range (from _spl_rent_range meta). Register these via manage_property_posts_columns and manage_property_posts_custom_column hooks. Register a shortcode [properties] with attributes city (term slug, default empty string), state (text, default empty string), limit (int, default 12). Build a WP_Query: post_type property, posts_per_page limit, post_status publish. If city is set add a tax_query for property_city with the term slug. If state is set add a meta_query for _spl_state LIKE matching the value. Loop the query and build the output: wrap everything in a div.spl-listings. For each post output a div.spl-card containing: h3 with an a tag linking to get_permalink, a span.spl-city with the property_city term names joined by comma, a p.spl-address showing the _spl_address meta and _spl_state meta together, a p.spl-details showing _spl_bedrooms and _spl_rent_range, a p.spl-excerpt with get_the_excerpt, and if get_option spl_contact_url is not empty an a.spl-enquire linking to that option value with target _blank and text Enquire. Call wp_reset_postdata after the loop. Return the output string. Output the following CSS via wp_head action with priority 20 using a style block: .spl-listings{display:flex;flex-wrap:wrap;gap:1.5rem;} .spl-card{flex:1 1 280px;border:1px solid #ddd;border-radius:6px;padding:1.25rem;background:#fff;} .spl-card h3{margin-top:0;} .spl-enquire{display:inline-block;margin-top:.75rem;padding:.5rem 1rem;background:#0073aa;color:#fff;border-radius:4px;text-decoration:none;} @media(max-width:600px){.spl-card{flex:1 1 100%;}} Add a submenu page under Settings with capability manage_options, menu slug simple-property-listings, title Property Listings Settings. Show a form with method POST, wp_nonce_field spl_settings, and one field: Contact URL (input type url, name spl_contact_url, value from get_option spl_contact_url). On POST with valid nonce and manage_options capability, update_option spl_contact_url using esc_url_raw on the submitted value. Show a success admin notice after saving.