今回から、「LWR (Build Your Own) × Agentforce」 を用いたB2B不動産ポータルサイト 「Real Estate Search」 の構築連載をスタートします。
本連載では、実際に動作しているコードをベースに、LWRサイト構築の勘所を解説していきます。 Vol.1のテーマは、サイトのメイン機能である「物件検索画面のUI構築」です。
Vol.1の今回は、サイトの顔となる「トップページ(検索結果一覧)」のUI実装について解説します。標準コンポーネントがほとんど存在しないLWRでは、検索画面全体をLWCで設計する必要があります。今回は保守性を高めるための「コンポーネント分割」と、SLDS (Salesforce Lightning Design System) を活用した「レスポンシブ・グリッド」の実装について紹介します。
今回の記事で作成したコンポーネントの全ソースコードをGitHubで公開しています。 ぜひ Clone して、あなたの組織で動かしてみてください。
データ構造のポイント(物件と住戸)
UIを作る前に、表示するデータの構造を少しだけ整理しておきます。今回は「建物」と「部屋」を分けて管理しています。
- 物件 (
Building__c): 建物情報(住所、最寄駅、築年数など) - 募集住戸 (
Listing__c): 部屋情報(賃料、間取り、平米数など)
今回の検索結果コンポーネントで表示するのは、従オブジェクトである 「募集住戸 (Listing__c)」 です。

カスタム住所項目の有効化
GitHubに共有したオブジェクトをそのままデプロイする前に、カスタムオブジェクトで住所型項目を使えるようにしましょう。以下の設定作業が必要です。
- カスタム住所項目を有効化
- [設定] > [ユーザーインターフェース] を開き、[カスタム住所項目を使用] にチェックを入れる。
- 州/国/テリトリー選択リストを設定
- [設定] > [州/国/テリトリー選択リスト] を開き、国や都道府県を日本語化する。
- カスタム住所項目を有効化
- 州/国/テリトリー選択リストを設定


検索結果一覧の全体像
完成した画面がこちらです。ホーム画面に検索機能を集めています。
※ヘッダーのヒーロー検索領域はVol.4で紹介します。
検索条件にヒットした物件がカード形式で並び、以下のように表示がレスポンシブに切り替わるUIになっています。
- PC:条件検索欄1列+検索結果3列
- タブレット:条件検索欄1列+検索結果1列
- スマホ:条件検索欄と検索結果が縦に1列
- PC
- タブレット&スマホ



画面全体のアーキテクチャ
検索画面は1つの巨大なLWCで作るのではなく、役割ごとに以下の3つに分割しました。
- Container (
propertySearchContainer): 親。データの取得、レイアウト枠の定義、状態管理。 - Filter (
propertySearchFilter): 子(左側)。検索条件の入力フォーム。 - Result (
propertySearchResult): 子(右側)。検索結果のカード表示。
これにより、「検索ロジック」と「表示デザイン」が分離され、コードの見通しが劇的に良くなります。

親コンポーネントでのレイアウト定義
まずは親となる propertySearchContainer です。 ここでは SLDS Grid System を使い、PC画面では「検索条件:結果一覧 = 3 : 9」の比率で表示し、スマホでは縦に積まれるレスポンシブ設定を記述します。
propertySearchContainer.html
<template>
<div class="slds-grid slds-wrap slds-gutters_x-small">
<div class="slds-col slds-size_1-of-1 slds-medium-size_6-of-12 slds-large-size_3-of-12">
<div class="slds-box slds-m-bottom_medium form-container">
<c-property-search-filter onsearch={handleSearch}></c-property-search-filter>
</div>
</div>
<div class="slds-col slds-size_1-of-1 slds-medium-size_6-of-12 slds-large-size_9-of-12">
<template if:true={isLoading}>
<div class="slds-is-relative slds-p-around_large">
<lightning-spinner alternative-text="Loading" size="medium"></lightning-spinner>
</div>
</template>
<template if:false={isLoading}>
<c-property-search-result listings={processedListings}></c-property-search-result>
<template if:true={isNoResults}>
<div class="slds-text-align_center slds-p-around_large">
<p>条件に一致する物件が見つかりませんでした。</p>
</div>
</template>
</template>
</div>
</div>
</template>
実装のポイント:
slds-large-size_3-of-12 と 9-of-12 を組み合わせることで、LWRサイト特有の幅広なキャンバスを有効活用しています。
データ取得(Apex呼び出し)や、CMS画像URLの生成ロジックはこの親コンポーネントのJSに集約させています。
検索結果カードのデザイン
次に、右側に表示される検索結果 propertySearchResult です。 不動産サイトにおいて写真は命です。ここでは「建物外観」と「部屋の内装」を2枚並べて表示するデザインを採用しました。
propertySearchResult.html
<template>
<div class="slds-grid slds-wrap slds-gutters_x-small">
<template for:each={listings} for:item="item">
<div key={item.Id} class={cardWrapperClass}>
<article class="slds-card slds-card_boundary h-100">
<div class="slds-card__body slds-card__body_inner slds-p-top_small">
<div class="slds-grid slds-gutters_xx-small slds-m-bottom_small gallery-container">
<div class="slds-col slds-size_1-of-2 image-wrapper">
<template if:true={item.buildingPhotoUrl}>
<img src={item.buildingPhotoUrl} alt="Exterior" class="property-photo">
</template>
<template if:false={item.buildingPhotoUrl}>
<div class="slds-align_absolute-center h-100 slds-text-color_weak">No Photo</div>
</template>
</div>
<div class="slds-col slds-size_1-of-2 image-wrapper">
<template if:true={item.listingPhotoUrl}>
<img src={item.listingPhotoUrl} alt="Interior" class="property-photo">
</template>
<template if:false={item.listingPhotoUrl}>
<div class="slds-align_absolute-center h-100 slds-text-color_weak">No Photo</div>
</template>
</div>
</div>
<h3 class="slds-text-heading_small slds-truncate" title={item.buildingName}>
<a href="#" onclick={handleNavigate} data-record-id={item.Id}>{item.buildingName} {item.Name}</a>
</h3>
<div class="slds-m-top_x-small">
<p class="slds-text-title_caps slds-text-color_weak">{item.FloorPlan__c} / {item.AreaSize__c}㎡</p>
<p class="slds-text-heading_medium slds-m-vertical_xx-small">
<lightning-formatted-number value={item.Rent__c} format-style="currency" currency-code="JPY"></lightning-formatted-number>
</p>
<p class="slds-text-body_small">
{item.nearestStation} 徒歩{item.walkMinutes}分
</p>
</div>
</div>
<footer class="slds-card__footer">
<lightning-button
label="詳細・空室確認"
variant="neutral"
class="slds-m-right_x-small"
onclick={handleNavigate}
data-record-id={item.Id}>
</lightning-button>
</footer>
</article>
</div>
</template>
</div>
</template>
propertySearchResult.css
画像の縦横比崩れを防ぐため、CSSで object-fit: cover を指定しています。これにより、アップロードされた写真のサイズがバラバラでも、カードの高さは綺麗に揃います。
.gallery-container {
height: 150px;
}
.image-wrapper {
height: 100%;
overflow: hidden;
background-color: var(--dxp-g-root, #f3f2f2); /* サイトの背景色変数を使用 */
}
.property-photo {
width: 100%;
height: 100%;
object-fit: cover; /* 画像のトリミング指定 */
}
LWRにおける画面遷移の実装
LWRサイトでレコード詳細画面へ遷移する場合、NavigationMixin の standard__recordPage を使用します。
propertySearchResult.js
標準のオブジェクトページ設定がLWRでも有効に機能するため、standard__recordPage を使うのが最も標準的かつ安全な実装です。
handleNavigate(event) {
event.preventDefault();
const recordId = event.target.dataset.recordId;
if (recordId) {
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: recordId,
objectApiName: 'Listing__c',
actionName: 'view'
}
});
}
}
まとめ
Vol.1では、実用的な不動産検索画面のUI実装を行いました。
- コンポーネント分割: 親(ロジック)と子(表示)を分離し、データフローを整理。
- SLDSグリッド: クラス指定のみで、複雑なレスポンシブ・レイアウトを実現。
- CSS:
object-fitで画像の見栄えを制御。
次回 Vol.2 では、クリックした先の「物件詳細画面」の構築に進みます。 標準コンポーネントが少ないLWR環境において、「物件画像スライダー(カルーセル)」をCSSアニメーションを使って自作する方法を紹介します。




読者の声