Nasaq
Components

Marketplace

A store with a featured strip, categories and search, a detail page with install header, tabs, sidebar and permissions, a publish form and a template gallery, built on the catalog store.

PreviewOpen ↗

Code

<Marketplace  categories={[{ id: "comms", label: "Messaging" }]}  listings={[{ id: "mail", name: "Send email", summary: "Send a templated email.", category: "comms", featured: true, permissions: [{ id: "mail.send", label: "Send email as you", risk: "high" }] }]}  onInstall={async (l) => { await api.install(l.id); }}  onPublish={async (draft) => { await api.submit(draft); }}/>

Storefront · beta

Live examples and controls: Marketplace in the lab.

Install

npx shadcn@latest add https://docs.nasaqui.com/r/marketplace.json

The full store around CatalogStore. Browsing is the catalog (search, category chips, sort, cards) with a featured strip above it. Opening an item shows a page instead of a sheet: install header, Overview / Changelog / Reviews tabs and a side column with details, permissions, links and tags. A Publish button opens a validated submit form, and a Templates view shows a gallery. It holds no data and calls no backend.

When to use

  • A store for extensions, plugins or apps where each item needs a real detail page and a publish flow.

When not to use

  • A simple grid with a side sheet: use CatalogStore alone.
  • Selling physical goods with a cart: use the product components.

Import

import { Marketplace } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"

Quick start

<Marketplace
  categories={[{ id: "comms", label: "Messaging" }]}
  listings={[{ id: "mail", name: "Send email", summary: "Send a templated email.", category: "comms", featured: true, permissions: [{ id: "mail.send", label: "Send email as you", risk: "high" }] }]}
  onInstall={async (l) => { await api.install(l.id); }}
  onPublish={async (draft) => { await api.submit(draft); }}
/>

Anatomy

Marketplace                 data-slot="marketplace"
├─ switcher                 Extensions / Templates, Publish
├─ featured strip           up to three featured listings
├─ CatalogStore             search, chips, sort, cards; onSelect opens the detail
├─ MarketplaceDetail        install header, Tabs, sidebar (details, PermissionList, links, tags)
├─ TemplateGallery          category chips, preview cards, Use template
└─ Dialog > PublishForm     name, summary, category, version, repository, price, tags, permissions

API

PropTypeDefaultDescription
listingsMarketplaceListing[]requiredA CatalogItem plus featured?, screenshots?, permissions?, changelog?, reviews?, links?, compatibility?, license?, size?.
categoriesCatalogCategory[]required{ id, label, icon? }.
templates? / templateCategories?MarketplaceTemplate[] / CatalogCategory[]noneAdds the Templates view.
permissionOptions?MarketplacePermission[]noneWhat an author can declare in the publish form.
onInstall? / onUninstall?(listing) => Promise<void | { error?: string }>noneReturn { error } to show why it failed.
onOpen?(listing) => voidnoneThe "Open" action once installed.
onPublish?(draft: PublishDraft) => Promise<void | { error?: string }>noneAdds the Publish button.
onUseTemplate?(template) => Promise<void | { error?: string }>noneAdds "Use template".
onSelectedChange?(id | null) => voidnoneFired when a detail page opens or closes.
labels?Partial<MarketplaceLabels> & { store?: Partial<CatalogLabels> }en/arOverride strings; store goes to the catalog.

MarketplaceDetail, PublishForm, TemplateGallery and PermissionList are exported for use on their own. Permission risk is low, medium or high; the list shows the riskiest first.

Examples

Detail page on its own

<MarketplaceDetail listing={listing} onInstall={install} onBack={() => router.back()} />

Accessibility

  • Cards and featured tiles are articles whose title is a button covering the card. Install is a separate control.
  • Permission risk is written out (Low, Medium, High) next to an icon. The form marks invalid fields with aria-invalid and a message; errors from the server use role="alert".
  • The dialog traps focus and closes on Escape.

RTL & i18n

  • English and Arabic built in. Versions, repository URLs, sizes and licences stay left-to-right in <bdi dir="ltr">.
  • The back arrow and external-link icon mirror.

Styling & tokens

  • bg-card, bg-secondary, border-border, rounded-card; hover --nq-hover. No raw hex.
  • Target [data-slot=marketplace], [data-slot=marketplace-detail], [data-slot=publish-form], [data-slot=template-gallery].

Do / Don't

  • Do list every permission an extension needs; the detail page is the user's consent screen.
  • Do mark at most three listings featured.
  • Don't put secrets in links or screenshots.

Lab

https://docs.nasaqui.com/?path=/docs/components-storefront-marketplace--docs

On this page