React components
The search box, listing and product pages for React 19, built on the client, with every part yours to restyle or swap.
@orbsearch/react draws a storefront in React 19.3 or a later React 19: the search box, a page for every listing, a product page and the parts they are made of. It brings the behavior, the semantics and a plain look, and reads every answer through the client:
import { , , type } from "@orbsearch/client";
import { , , , } from "@orbsearch/react";
import type { } from "react";
const = ({ : "http://127.0.0.1:7800" });
export function ({ }: { : }) {
return (
< ={} ="de">
{}
</>
);
}
export function ({ , }: { : ; : string }) {
if (()) return < ={(, { , : "de" })} />;
if (.) return < ={.} ={[{ : .., : }]} />;
return null;
}StorefrontProvider holds the client, the language and your router's navigate; without one, a link loads the next page. page is what resolve answered, and listingView reads a listing out of it. Your storefront is the whole shop in React Router.
The two pages
ListingPage draws a search, category or brand page from listing, the view listingView reads out of resolve's answer. It takes a few props beside it:
filters:"bar", the default, for quick filters above the results and all of them in a sheet, or"sidebar"for a panel beside the results.tileandbanner: your own, built from the parts (Customize).sorts: the sort codes to offer instead of the answer's, the first where the page starts.sectionTitle,departmentsandtip: the heading of a section beside the products, where an empty search sends the shopper, and your advice when nothing is found.busy: your router says the next page is on its way, asuseNavigationdoes.
What only your shop knows is empty in the view and yours to set, as { ...view, trail, categories, popular }: your home in the trail, your category navigation and the products an empty search offers.
ProductPage takes the product and its trail, the categories from the top down with the product last. variant is the variant the URL names, read with variantAt(product, url)?.id; more adds a rail of other products, and buyBox replaces the part a shopper decides on.
The parts
Each block of the pages is a part with one job, exported on its own for a layout of your own:
| Part | What it is |
|---|---|
SearchBox | The box with suggestions, a GET form underneath |
ListingToolbar, CategoryLinks, SectionLinks | The filters, count and sort; the categories one level on; the hits beside the products |
ProductResults, ProductGrid, ProductTile, ProductRail | Results with "Load more", the grid, the tile, a row that scrolls sideways |
Banner | A banner or teaser a rule places |
FilterBar, FilterPanel, FilterSheet | Quick filters above the results, a panel beside them, a modal sheet |
FacetList, FacetFilter, GroupFilter | Every facet, one facet, one group of facets |
AppliedFilters | A chip per applied filter, which takes it back |
NoMatches, NoResults | Filters that leave nothing, a search that finds nothing, each with a next step |
ProductGallery, BuyBox, ProductInfo | A product's photos, what a shopper decides on, its details |
SortSelect, Breadcrumb, Price, Rating, ProductImage | The small parts |
Hooks sit below them: useListing holds a listing's state, useFacet one facet's values and semantics, useFilters a pending choice with its count, and useLiveFilters a choice that applies after a short pause.
Change an element
To change a part's element or classes and keep its semantics, use Base UI's render prop:
<. ={() => < {...} ="shop-price" />} />;Root, SponsoredLabel, Media, Badges, Badge, Brand, Price and Facts take render; Title, Image and Rating take a className. The search box takes groups to reorder or add groups of suggestions, and suggestion for a row of your own built on SuggestionItem. To change what a part holds, swap it.
Every ad is marked
A product a sponsored pin placed starts its name with "Anzeige" (ad) or "Sponsored", messages.sponsored, so the tile marks the ad where its name is seen and read. Restyle the label through the title:
<. ={<. ="shop-ad" />} />;To show it elsewhere in the tile, pass label={null} and place <ProductTile.SponsoredLabel /> yourself, since the law wants every ad marked. A suggestion row of your own renders <SuggestionSponsoredLabel suggestion={suggestion} /> before the name.
Facet displays
One registry of displays draws every facet, keyed by name, in the bar, the panel, the sheet and a layout of your own. Ours are exported as facetDisplays: list, stars, images, swatch, grades, buttons, select, range, slider, histogram, hierarchy and toggle.
A facet gets the display its release names in custom_display, else the one display names, else its kind's own. The shop's displays, passed to StorefrontProvider as displays, come before ours, so a storefront without yours still draws the facet as display says. A group of facets draws from groupDisplays, where ours is finder. Customize builds a display of your own.
Reference: custom_display · display
Facets that load when opened
The Query API counts a page's first 30 facets and lists the rest as deferred, and cuts a long list to 50 values with more, since every counted facet costs the engine on each request. ListingPage wraps its parts in LateFacets, so the rest arrive when a shopper wants them:
import { , , , type } from "@orbsearch/react";
export function ({ }: { : }) {
const = ();
return (
< ={{ ...., : . }}>
< ={.} ={.} ={.} />
</>
);
}A facet the shopper opens, or whose "Show more" they press, asks for its values with search.facets, under the choices the page shows, and again when they change. Meanwhile it says "Loading values …", or that they could not be loaded. A facet in a popover of your own calls useLoadWhenOpen. Without LateFacets, facets draw as given.
Reference: deferred · more · counted_facets
Words and photos
The components speak German for de and its regions and English for every other locale. Pass your own messages to change a word or add a language, and images to load photos through your image host's widths:
import type { } from "@orbsearch/client";
import { , , type } from "@orbsearch/react";
import type { } from "react";
const = { ...("de"), : "Sofas, Teppiche, Leuchten" };
const : = { : [320, 640, 1024], : (, ) => `${}?w=${}` };
export function ({ , }: { : ; : }) {
return (
< ={} ="de" ={} ={}>
{}
</>
);
}The placeholder lists sofas, rugs and lights. Without images, photos load at the size the catalog's URL gives, and nouns names in your words the types a listing counts.
Next
- Customize: tokens, slots, swapped parts and components of your own.
- Recipes: one facet in a layout of your own, filters in a sheet on a phone.
- Accessibility: what the components do for keyboard and screen reader users.