Widget Catalog
The Widget Catalog provides a browsable and searchable list of all available widgets that can be added to your layouts. It is accessible from the editor sidebar and through the Widget Selection Modal.
Overview
Widgets are organized into categories (e.g., Content, Data, Layout). Each widget entry displays its title and, optionally, an expandable description to help you identify the right widget for your use case.
Searching for Widgets
You can filter the displayed widgets by typing in the search box at the top of the catalog or modal.
- Matches against: widget title and widget description.
- Case-insensitive: the search is not case-sensitive.
- Live filtering: results update as you type β no need to press Enter.
- Empty query: when the search box is empty, all widgets are shown grouped by category.
- No results: if no widgets match your query, an empty state message is displayed.
Widget Catalog (Sidebar)
The search input is located at the top of the sidebar panel.
[ π Search... ]
Layout
βββββββββββββββ βββββββββββββββ
β Container β β Grid β
βββββββββββββββ βββββββββββββββ
Content
βββββββββββββββ
β Text β
βββββββββββββββ
When you begin typing, categories with no matching widgets are hidden automatically. Clearing the search box restores the full list.
Expanding a widget's description (via the info button) is reset each time you type a new search term.
Widget Selection Modal
The search input appears below the modal header, above the widget grid.
ββββββββββββββββββββββββββββββββββββββββββββ
β Select a Widget [ β ] β
β [ π Search... ] β
β β
β Layout β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β βContainer β β Grid β β ... β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββ
When the modal is closed (either by selecting a widget or pressing the close button), the search query is automatically cleared so the next time it opens the full list is shown.
How Filtering Works
Filtering is performed by the shared getFilteredWidgetsByCategory function in widget-registry.ts. It:
- Iterates over all registered widget categories.
- For each category, retains only widgets that are marked
visibleInCatalog. - If a search query is present, further filters to widgets whose translated title or description contains the query string.
- Excludes categories that have no matching widgets from the result.
Because the result is derived reactively from the search query state, the UI updates automatically as the user types.
Third-Party Web Component Entries
In addition to the code-registered widgets, the catalog dynamically lists the current project's
active web component registrations (see the web component registry). Each entry uses the
registration's title and category and β when placed β creates a WebComponent widget
preconfigured with that registration's id. The component then renders inside the sandboxed
iframe via the host bridge. Building and registering your own components is covered in the
Web Components docs (authoring, permissions,
security model, consent, and
theming).
- Only registrations with status
activeappear;draftanddisabledregistrations are hidden. - A registration with an unknown category is listed under Content.
- Search matches the registration title, like any other widget.
- Placed widgets of a registration that is later disabled render a neutral placeholder instead of loading any third-party code.
- Before any third-party code runs, each user sees a consent overlay stating that this is a third-party component smallstack is not responsible for, listing the permissions the component declared. Only after accepting does the sandbox load. Consent is per user, per registration and per bundle version and per declared permission set β uploading a new bundle version or changing the registration's permissions re-prompts every user. For logged-in users the consent is recorded server-side; anonymous application visitors keep it in their browser's localStorage.
The merging is performed by mergeWebComponentCatalogEntries in
packages/client/src/web-components/web-component-catalog.ts on top of the filtering described
above.
Datatype bindings at placement time
A registration's collection scopes (collection:{configurationProperty}:{action}) name a
property of the widget's placement configuration β not a concrete datatype. When a placed
WebComponent widget is edited, the widget properties form expands each declared configuration
property into a datatype picker (under "Data bindings"), so the editor binds the concrete
datatype there. The picker stores the bare type id, which server-side scope resolution
(mapScopeToRLSPermission) maps onto the platform's RLS permissions. Without a binding, the
scope is simply not granted and the consent overlay falls back to showing the raw configuration
property name.
Attributes
| Attribute | Description |
|---|---|
data-testid="widget-catalog-search" |
Test ID for the search input in the Widget Catalog sidebar. |
data-testid="widget-selection-search" |
Test ID for the search input in the Widget Selection Modal. |
data-testid="catalog-widget-web-component-{registrationId}" |
Test ID of a dynamic web component catalog entry. |
data-testid="web-component-widget" |
Root of a placed WebComponent widget. |
data-testid="web-component-widget-placeholder" |
Neutral placeholder shown for disabled/missing registrations. |
data-testid="web-component-widget-loading" |
Skeleton shown while the registrations collection does its initial sync. |
data-testid="web-component-consent-overlay" |
Consent overlay blocking the sandbox until accepted. |
data-testid="web-component-consent-permission" |
One disclosed permission entry in the consent overlay. |
data-testid="web-component-consent-no-permissions" |
Shown instead of the permission list when a registration declares none. |
data-testid="web-component-consent-accept" |
Accept button of the consent overlay. |
Related
- Widget Selection Modal β modal variant of the catalog used when inserting widgets inline.
- Widget System β overview of widget architecture and registration.