Skip to content

Hero search

Status: ✅ Implemented

Give users whose task starts with a search — find a hotel, a product, a candidate — a focused landing page: a big hero header, a prominent search box with filter facets, and results as cards.

Dropping users on a dense listing when their task is “search for X” buries the search box in toolbar chrome. Building a custom search landing by hand means rewiring search, facets, pagination and result rendering that Mateu’s listing machinery already provides.

Extend HeroSearch<Filters, Row> and implement search(SearchRequest, HttpRequest) exactly like any listing (the archetype already declares Searchable and Filterable<Filters> for you). It renders a centered hero (title + subtitle, optional background image) followed by the standard searchable listing, with results as card tiles by default.

@UI("/hotel-search")
@Title("Hotel search")
public class HotelSearch extends HeroSearch<HotelFilters, Hotel> {
public record HotelFilters(String zone, Integer minStars) {}
public record Hotel(String name, String zone, int stars, String price) {}
@Override protected String heroTitle() { return "Find your hotel"; }
@Override protected String heroSubtitle() { return "Search by name or zone…"; }
@Override
public ListingData<Hotel> search(SearchRequest request, HttpRequest httpRequest) {
// query your use case / repository with
// request.searchText() + filters(request) + request.pageable()
return ListingData.<Hotel>builder().page(/* … */).build();
}
}

Hotel search

  • The Filters record fields become the facets of the search box (the standard smart search bar: they live in its Filter by panel and show as removable chips when applied).
  • Results render as cards by default — override gridLayout() to return GridLayout.table or any other layout.
  • The listing starts empty (distraction-free) and searches on enter. Add @Trigger(type = TriggerType.OnLoad, actionId = "search") on the class to preload results.
  • Override heroImage() to set a background image for the hero (a dark overlay keeps the text readable).

HeroSection is a regular component: compose it with a fluent Listing (or anything else) from ComponentTreeSupplier:

HeroSection.builder()
.title("Find your hotel").subtitle("")
.image("/images/hero.jpg").centered(true)
.build()

What the Redwood hero-search-page template exposes, and what Mateu gives you for it. The canonical page-header elements shared by every template are documented once in Page templates.

Legend: ✅ supported · 🟡 partial · — not supported · ⚪ deliberately out of scope

Redwood prop / slotMateu
pageTitleheroTitle()
descriptionheroSubtitle()
Hero backgroundheroImage() (dark overlay keeps the text readable) — a Mateu addition, the Redwood hero has no image prop
collectionView: on | offthe listing always renders; it just starts empty until the user searches🟡
selectionrow clicks dispatch the listing’s own actions🟡
avatarPageDto.avatar/icon on the canonical header
suggestions[] (suggestion chips under the search box) + spSuggestionAction
descriptionHotspot {label, data} + spDescriptionHotspotAction (clickable text inside the description)— the subtitle is plain text
Slot default (results)the listing; cards by default, gridLayout() to change it
Slot searchTemplatethe standard smart search bar, with the Filters fields as facets
Slot detailTemplate— pair it with collection detail when you need an in-place detail pane
Slot announcement (aria-live)live regions are installed client-side for a11y, but the backend cannot declare announcement content🟡

Use hero search when searching is the task and the page is an entry point (public catalogs, pickers, knowledge bases). Prefer a plain AutoCrud/Listing when the user works the collection (bulk edits, exports, dense scanning).