Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Blocks

Marketplace Search Page

Blocks

Marketplace Search Page React block for Tailwind CSS v4

Marketplace Home PageMarketplace Shell
Live props
Content
pageSize24
language
locale
currency
State
defaultView
defaultFiltersOpen
defaultSavedSearch
loading
Behavior
viewPersistKey
loadMode
animateIn
Layout
showPromoted
showRelated
filterMode
Appearance
tone
radius
Code
import { MarketplaceSearchPage } from "@/components/blocks/marketplace-search-page"

<MarketplaceSearchPage />
  1. Home
  2. Components
  3. Blocks
  4. Marketplace Search Page
Pro

Marketplace Search Page React block for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React marketplace search results page: count, facets in a sidebar or phone sheet, filter chips, sort, grid or list and endless results.

The breadcrumb leads back to Home and the parent category; the title names the category and the place (Bikes in Cluj-Napoca) and the count under it is announced after every change. From 1024px the filters sit in a sticky sidebar and apply at once: drag the price thumbs over the histogram or pick a preset, tick Condition or Seller (each option shows how many listings it would give), choose the place with a radius (Only here to +100 km, off for All locations), switch Delivery available, choose Posted and the wheel sizes. Below 1024px the Filters button (with the active count) opens a full height sheet whose button counts the draft (See 312 listings) and applies it; Reset clears the draft, Escape or a swipe down closes it. Active filters and the query show as chips under the header (sideways scroll on phones); × removes one, Clear all removes all. Sort opens a menu (a sheet on phones), the grid or list switch cross-fades the results, the bell saves the search with a toast and pressing it again stops following it. Up to three promoted listings lead the results (a swipeable rail on phones). The next 24 listings load as you near the end (or with Show 24 more in button mode); a failed page shows Try again. Filters that match nothing show an empty state with Clear filters. Hearts save a listing at once. Related searches links close the page on laptops. Sections rise in once on mount; reduced motion skips it.

Marketplace Search Page is a Pro React block in the React blocks category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add marketplace-search-page, together with Breadcrumb, Button, Empty State, Filter panel, Listing card and 13 more and the npm packages motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add marketplace-search-page

Marketplace Search Page is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import MarketplaceSearchPage from @/components/blocks/marketplace-search-page.

// app/(market)/search/page.tsx (the shell lives in app/(market)/layout.tsx)
"use client"

import { useRouter, useSearchParams } from "next/navigation"
import {
  MarketplaceSearchPage,
  type MarketplaceSearchState,
} from "@/components/blocks/marketplace-search-page"

export default function SearchPage() {
  const router = useRouter()
  const params = useSearchParams()
  const state: MarketplaceSearchState = {
    query: params.get("q") ?? "",
    category: params.get("cat") ?? "bikes",
    location: params.get("loc") ?? "cluj-napoca",
    filters: JSON.parse(params.get("f") ?? "{}"),
    sort: params.get("sort") ?? "newest",
    view: params.get("view") === "list" ? "list" : "grid",
  }
  return (
    <MarketplaceSearchPage
      state={state}
      onStateChange={(next) => {
        const query = new URLSearchParams({
          q: next.query,
          cat: next.category,
          loc: next.location,
          f: JSON.stringify(next.filters),
          sort: next.sort,
          view: next.view,
        })
        router.replace(`/search?${query}`, { scroll: false })
      }}
      onSaveSearch={(search, saved) => fetch("/api/saved-searches", { method: saved ? "POST" : "DELETE", body: JSON.stringify(search) })}
    />
  )
}

Props

  • state
    MarketplaceSearchState

    The search, controlled with onStateChange: { query, category, location (a ro-locations city id, "" for the whole country), filters (FilterValues keyed by filter id), sort, view }. A real app writes it to the url.

  • defaultState
    Partial<MarketplaceSearchState>

    The starting search while uncontrolled (DEFAULT_MARKETPLACE_SEARCH_STATE: Bikes in Cluj-Napoca, no filters, newest first, grid).

  • onStateChange
    (state: MarketplaceSearchState) => void

    Every change: a filter, a chip, the sort, the view, the place.

  • filters
    FilterDef[]

    The facets. Default: the sample set of the category (createSearchFilters): Price with a histogram, Condition, Location with a radius, Delivery available, Seller, Posted and Wheel size for bikes; Price, Rooms, Floor area, Location, Floor, Layout, Seller and Posted for apartments.

  • items
    MarketplaceSearchListing[]

    The local ads the default search runs over (ListingData plus cityId, facets and keywords). Default: 480 generated ads of the category (getSampleListings, l01 included).

  • search
    (state: MarketplaceSearchState, page: number) => (MarketplaceSearchResult | Promise<MarketplaceSearchResult>)

    Plug the server here: { items, total, promoted?, histogram?, counts? }. Page 0 replaces the results (skeletons while pending), later pages append. Default: searchListings over items during render, the next pages after 700ms.

  • pageSizedefault 24
    number

    How many ads each page or Load more step adds, clamped to 6-96.

  • defaultViewdefault "grid"
    "grid" | "list"

    The first view while the state names none.

  • viewPersistKeydefault "northwind-market-search-view"
    string

    Remembers grid or list in localStorage while the state is uncontrolled; "" turns it off.

  • columns
    ListingGridColumns

    Cards per row at the grid's own width (container queries). Default { base: 2, md: 3, xl: 3 }: two on phones, three beside the filters.

  • showPromoteddefault true
    boolean

    Up to three promoted ads above the results.

  • showRelateddefault true
    boolean

    The Related searches links under the results (from 1024px).

  • relatedSearches
    string[]

    The related searches. Default: the category's sample (E-bike, MTB 29...).

  • loadModedefault "auto"
    "auto" | "button"

    auto: the next page loads as the end nears (the button stays as a fallback); button: Show 24 more.

  • filterModedefault "auto"
    "auto" | "sidebar" | "sheet"

    auto: a sticky sidebar from 1024px and a sheet below; sidebar: always beside the results (above them on phones); sheet: the Filtre button and a sheet at every width.

  • filtersOpen
    boolean

    The filters sheet, controlled with onFiltersOpenChange.

  • defaultFiltersOpendefault false
    boolean

    Opens the filters sheet on mount (where a sheet is used: below 1024px, or filterMode sheet).

  • onFiltersOpenChange
    (open: boolean) => void

    The sheet opened or closed.

  • savedSearch
    boolean

    The bell's state, controlled. Uncontrolled it belongs to one search and resets when the search changes.

  • defaultSavedSearchdefault false
    boolean

    The first search starts saved (the bell is on).

  • onSaveSearch
    (state: MarketplaceSearchState, saved: boolean) => (void | Promise<unknown>)

    The bell. A promise keeps it busy; resolved, a toast says Search saved; rejected, an error toast. Default: a simulated 600ms request.

  • onFavoriteChange
    (id: string, favorite: boolean) => (void | Promise<unknown>)

    A heart was toggled; it shows at once and a rejected promise rolls it back.

  • languagedefault "en"
    "ro" | "en"

    The sample content's language, kept for compatibility: the default listings, filter names, category names and related searches are English in both. Copy comes from labels.

  • labels
    Partial<MarketplaceSearchPageLabels>

    Every visible string, English by default (DEFAULT_MARKETPLACE_SEARCH_PAGE_LABELS; MARKETPLACE_SEARCH_PAGE_LABELS_EN is the same set under its older name), with nested sets for the grid, the cards, the filter panel and the breadcrumb.

  • localedefault "en-US"
    string

    Intl locale for the count, prices and dates.

  • currencydefault "USD"
    string

    The default listings' prices (converted at fixed rates; USD, EUR or RON) and the price filter.

  • now
    Date | number

    The clock for Today at 2:32 PM and Posted. Default: useNow() (the sample's Sep 24, 2026, 4:40 PM while hydrating); the demo passes the same.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The filters' checks, chips, sliders and apply button, the filter count badge, the empty state action and the hearts.

  • radiusdefault "md"
    "sm" | "md" | "lg"

    The listing cards and the empty state: 12 / 16 / 24px.

  • loadingdefault false
    boolean

    The first page: the count and the grid as skeletons of their real shape.

  • animateIndefault true
    boolean

    The title and the results rise in once when the page mounts on the client; new pages fade in (never over server HTML, never with reduced motion).

  • linkComponent
    (props: LinkComponentProps) => ReactNode

    The router link for the breadcrumb, the cards and the related searches. Default: the provider's (the shell's), then <a>.

  • className
    string

    Classes for the root.

  • classNames
    Partial<Record<"top" | "title" | "count" | "body" | "filters" | "results" | "related" | "empty", string>>

    Classes for the parts.

Parts

  • DEFAULT_MARKETPLACE_SEARCH_PAGE_LABELS

    constant

    Every English string of the results page: title, count, sort options, filters, empty state and paging. Spread it and override what you translate.

  • MARKETPLACE_SEARCH_PAGE_LABELS_EN

    constant

    An alias of DEFAULT_MARKETPLACE_SEARCH_PAGE_LABELS, kept so older imports keep working.

  • searchListings

    function

    Applies the query, location, filters and sort of a search state to a local list of listings. Use it for a client-side search or as the reference for your API.

  • createSearchFilters

    function

    Builds the facets of one category in sidebar order, with prices in the given currency. The page uses it when you pass no filters.

  • getSearchCategory

    function

    Returns a sample category by id (label, parent categories, filter set and related searches) in English or Romanian; an unknown id falls back to the last entry.

  • generateListings

    function

    Generates the listings of one category, newest first: 480 by default and always the same for a given seed, language and currency.

  • getSampleListings

    function

    generateListings memoized per category, language and currency; the page's default data.

  • DEFAULT_MARKETPLACE_SEARCH_STATE

    constant

    The starting search state of the demo: category, query, location, filters, sort and view.

  • MARKETPLACE_SEARCH_SAMPLE

    constant

    The demo content: the starting state, the filter sets for bikes, apartments and any other category, and the listing helpers. Replace it with your own data.

  • MARKETPLACE_SEARCH_NOW

    constant

    The fixed "now" of the sample (September 24, 2026, 4:40 PM) that relative dates are measured from, so server and client render the same.

  • USD_RATES

    constant

    Dollars per unit of each sample currency (USD, EUR, RON), used to convert the sample prices.

  • RON_PER_EUR

    constant

    The rounded RON per euro rate the sample uses to convert between its currencies.

  • mulberry32

    function

    A tiny seeded random number generator, so the generated listings are the same on the server and the client.

Examples

Open

List view, two filters

Rows with the seller line; Price $100 to $300 and Ships are on as chips, remove one to widen the list.

Open

No results

Filters that match nothing: the empty state clears them in one tap.

Open

Filters in a sheet

filterMode sheet with the sheet open and brand accents; the button counts the draft before you apply it.

Open

Apartments in Cluj-Napoca

Apartments for sale with its own facets (Rooms, Floor area, Floor, Layout) and euro prices.

Open

Euro prices

en-GB formatting and the same listings priced in euro.

Open

Load more fails

A custom search with a button: the second page fails once, the retry brings it in.

Open

Loading

The count and the grid as skeletons of their real shape.

Open

Every location, four columns

No place, cheapest first, the filters in a sheet at every width, info tone and large radius.

Dependencies

motionlucide-reactbreadcrumbbuttonempty-statefilter-panellisting-cardlisting-gridprice-rangetagtoastuse-controllable-statemotionnavigationnumber-formatrelative-timero-locationstext-matchtonesui-config

Files

  • components/blocks/marketplace-search-page.tsx
  • components/blocks/marketplace-search-page-data.ts

FAQ

Is the React Marketplace Search Page block free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Marketplace Search Page?

Run npx wingo-ui@latest add marketplace-search-page. It also adds the Wingo UI items it builds on (Breadcrumb, Button, Empty State, Filter panel, Listing card and 13 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/marketplace-search-page.

How does Marketplace Search Page behave on phones?

Active filters and the query show as chips under the header (sideways scroll on phones); × removes one, Clear all removes all. Sort opens a menu (a sheet on phones), the grid or list switch cross-fades the results, the bell saves the search with a toast and pressing it again stops following it.

See also

Marketplace ShellMarketplace Home PageFilter panelListing gridListing cardPrice range

More React blocks

Order History PagePost Listing WizardPricing PageProfile PageRegister PageSettings Page
All 26 React blocks

Articles using this component

  • React Marketplace Template That Works Like an App

    Build it: screens and apps · Oct 16, 2026 · 15 min read

PreviousMarketplace Home PageNext Marketplace Shell