Apps Are Everywhere Now: Extension Updates in Saleor Dashboard
App extensions are moving out of menus and into the places you already work — the command palette, expandable widgets, and direct app-to-app navigation.
In June we wrote that apps can live on the Dashboard home page. This is the follow-up, and it points in a different direction: an app no longer has to be a destination at all.
Until now, reaching an app extension meant knowing where it was — a tab, a page, an item in a "more actions" menu. Three new mechanisms change that. App actions now appear in the command palette you already type into. Widgets can start compact and expand on demand. And apps can hand off to each other directly.
Everything below needs Saleor Dashboard 3.23.28 and @saleor/app-sdk 1.15.0, except where a lower version is noted.
App actions in the command palette
A year ago we rebuilt Cmd+K into a proper command palette. It could navigate anywhere in the Dashboard, but it could not reach anything an app provided. That half is now in place.
The new SEARCH_ACTION mount point puts app actions directly in the palette:
POPUP, NEW_TAB, and APP_PAGE targets all work. WIDGET does not — a widget has no palette to render into.
Actions are shown only to staff who hold the extension's declared permissions, so the palette stays honest: if you cannot run it, you do not see it.
Scoping to a view
By default an action is available everywhere. options.views narrows it to the pages where it makes sense:
CHANNEL_DETAILS is new, and joins views like PRODUCT_DETAILS and ORDER_DETAILS. It covers a case apps ask about often: configuration that only means something for one channel, such as a payment app's per-channel setup.
When an action opens from a detail page, that page's entity comes with it. A PRODUCT_DETAILS action receives productId, a CHANNEL_DETAILS action receives channelId holding the channel's global id — the same context the extension would get from that page's "more actions" menu. If you need a channel slug, resolve it from the id.
Finding actions by what users call them
Labels are written by app developers. Search terms are typed by staff, and the two rarely match. options.aliases closes that gap:
Someone typing taxes finds it. Aliases are matched but never displayed, and matching is case-insensitive and typo-tolerant like the rest of the palette. The option is valid only on SEARCH_ACTION; setting it on another mount fails manifest validation.
Widgets that expand
A widget has limited room. Anything richer than a summary used to mean sending the user to a full app page and losing the context they were working in.
A WIDGET extension can now dispatch OpenPopup to expand into one of its own app's POPUP extensions on the same page:
The compact card stays the default view. The full one opens when the user asks for it, still inside the page they were on.
Two supporting pieces make this work. Manifest extensions now accept an optional identifier field, unique per app — that is what extensionIdentifier points at. And params accepts any JSON-serializable value, which the Dashboard forwards verbatim into the popup. The app reads it back through appBridge.getState().appParams.
Worth knowing before you build on it: params is base64-serialized into the popup's iframe URL and capped at 2048 characters, so pass identifiers rather than payloads. The popup always opens with a fresh access token and fills the full popup height.
Apps that hand off to each other
Apps have been islands. Each one knew its own URL and nothing else, so sending a user from one app to another meant hardcoding a link and hoping the installation matched.
RedirectToApp removes the guesswork:
The Dashboard resolves the manifest identifier to the installed app, builds its URL, appends the optional path, and navigates in the same tab — the same way the existing redirect action behaves.
It is a small action, and it is the first primitive for apps composing with each other rather than each standing alone. A tax app can send you to the channel app that owns the setting it depends on. An analytics app can hand off to the product app that owns the record.
Your home page, your widgets
More surfaces for apps is only an improvement if staff keep control of what actually appears. So widget extensions can now be pinned or hidden per user.
Hover controls next to each widget handle it inline, and a new Extensions visibility section on your account page lists everything in one place. Pinned widgets sort to the top; hidden ones are not rendered at all.
Preferences are stored per staff user. An operations lead and a merchandiser installing the same set of apps end up with different home pages, without either of them uninstalling anything.
Also fixed
- App extensions in "more actions" menus now show the app's logo, with a placeholder when the app has no logo (#6852).
- A popup now closes when the app dispatches a
redirectto a Dashboard page. It used to stay open on top of the page you had just navigated to (#6850). - Re-installing an extension you already have says so and links you to open it, instead of failing with a duplicate-install error (#6805).
- Switching home tabs no longer reloads the embedded app — the last tab you opened stays ready in the background (#6821).
- App tokens are renewed based on when they actually expire, with a catch-up when you return to a long-idle tab. Installed apps could previously keep running with an expired token (#6795).
- Entity ids are no longer double-encoded when an app opens from the command palette. Actions used to receive ids like
UHJvZHVjdDo3Mw%3D%3Dand fail withInvalid ID(#6793). - The home page no longer flashes an empty "Welcome" screen before extensions load (#6700).