SeriesPart 54 of Building a property procurement platform on MedusaView the cluster →
Medusa & ArchitectureArticle

Your marketplace needs an admin and a supplier portal. Do you really want two frontend deployments?

How we delivered distinct admin and supplier React products through one custom Medusa Cloud dashboard build, with clean hosts and legacy links.

We Are Souk article cover: Your marketplace needs an admin and a supplier portal. Do you really want two frontend deployments?
Souk EngineeringCommerce architectureAug 2026·9 min read
Key takeaways
  • An internal marketplace operator and an external supplier do not use the same product.
  • The operator reviews suppliers, manages organisations, investigates workflows and governs the catalogue.
  • Giving both audiences the same navigation with a few hidden links creates a confusing product.
  • For a marketplace running on Medusa Cloud, we built two distinct React applications inside one custom dashboard delivery.

The client problem

An internal marketplace operator and an external supplier do not use the same product.

The operator reviews suppliers, manages organisations, investigates workflows and governs the catalogue. The supplier imports products, manages orders, updates commercial data and follows the work relevant to its own business.

Giving both audiences the same navigation with a few hidden links creates a confusing product. Building two completely independent frontends creates another problem: two deployment pipelines, two environment matrices, two asset configurations and two release trains attached to one commerce backend.

For a marketplace running on Medusa Cloud, we built two distinct React applications inside one custom dashboard delivery. Dedicated admin and vendor hostnames select the right panel. Each panel owns its router and product surface. Legacy /app/admin and /app/vendor links continue to work. Medusa's backend-controlled Vite build ships the bundle with the server.

The client gets two operational products without paying the operational cost of two unrelated frontend platforms.

The client problem: separate audiences, shared platform

The supplier portal was not a smaller admin panel.

Its users needed a focused workspace for product imports, catalogue preparation, orders, pricing and account tasks. Internal operators needed broader marketplace governance and support tooling. The information architecture, routes and default landing pages had to reflect those different jobs.

At the same time, both panels spoke to the same Medusa backend, used the same design language and shared foundations such as authentication clients, environment signals and reusable UI primitives.

Deploying them independently would make every backend release answer avoidable questions. Which panel version matches this API? Were both environment variable sets updated? Did the admin deployment finish while the supplier deployment failed? Do generated email links point at the correct version and host?

The desired boundary was product separation without delivery duplication.

Replace the dashboard entry, not the commerce engine

Medusa already owns an admin application build within the backend deployment. The project uses that extension point rather than standing up an unrelated static-hosting architecture.

In the backend configuration, the Medusa dashboard package entry is aliased to the project's own React entry file. Dashboard CSS, route aliases, shared components and public assets are resolved from the custom dashboard workspace. The backend prebuild prepares the dashboard before the Medusa build runs.

Medusa Cloud therefore remains responsible for building and serving the administrative frontend artefact. The custom code controls what application starts inside that artefact.

This keeps the release unit coherent. A backend change and the panels designed for it travel through the same Cloud build. The marketplace does not need a second hosting provider merely because its operational interface is richer than the default admin.

Select the product from the public hostname

The shared entry point inspects the browser hostname before mounting React.

An admin.* host selects the internal administration application. A vendor.* host selects the supplier application. On those dedicated hosts, both products live at / from the user's perspective.

The hostname is a clean product boundary for navigation and branding. Suppliers do not see an internal-looking /app/vendor prefix. Operators receive an equally direct admin URL. Bookmarks and copied links describe the product people are actually using.

If the browser is on the older shared host, the entry falls back to the path. /app/admin selects administration and /app/vendor selects the supplier panel.

If neither a recognised hostname nor a legacy path identifies a product, the entry chooses an explicit fallback instead of rendering an accidental mixture.

Give each panel its own router root

React Router needs to know which part of the browser path belongs to the application.

On a dedicated hostname, the selected panel receives / as its basename. A supplier order can therefore live at /orders/..., and an admin workflow can live at /workflow-executions/....

On the shared compatibility host, the same supplier router receives /app/vendor; the admin router receives /app/admin. The route definitions inside each application do not need two copies. The entry supplies the correct external base for the current delivery context.

This is more than URL polish. Hard-coded path prefixes spread quickly through login redirects, product links and navigation helpers. Passing the basename into each application keeps the routing contract close to the deployment decision.

Both products can evolve their own route maps while the shared entry remains responsible only for selecting and mounting one of them.

Load only the application the user asked for

The entry does not eagerly import both complete panels before deciding which one to render.

After resolving admin versus vendor, it uses a lazy import for the selected sub-application. Vite can place those entry branches in separate chunks, and the browser follows the branch required for the current host.

This protects the conceptual boundary as well as initial loading. The supplier product does not need to initialise the internal admin application, and the admin product does not need to mount supplier routes.

The evidence supports lazy entry loading, not a claim that the unused product contributes literally zero bytes to every generated artefact. Shared runtime and common dependencies still exist. The meaningful design decision is that application initialisation follows the chosen product rather than booting both and hiding one.

Share foundations without merging the products

Admin and vendor code live in separate source trees with separate routers and application roots. Common components occupy an explicit shared surface.

That arrangement enables deliberate reuse: an environment banner, foundational UI primitive or cross-product visual pattern can be shared without forcing both audiences into one route tree.

The rule is practical. Share a component when it represents the same concept and behaviour in both products. Keep it local when the business meaning differs, even if the screens look superficially similar.

This avoids two bad extremes. Copying everything creates visual and maintenance drift. Sharing everything creates components full of role flags and conditional behaviour that make neither product easy to understand.

One repository and one build do not require one undifferentiated application.

Keep authorisation on the server

A hostname, lazy import or hidden navigation item is not a permission system.

The admin and vendor panels create the correct user experience, but backend authentication, actor type, tenant scope and API middleware must still decide what data and actions each user may access.

This matters precisely because both panels ship from one delivery artefact. Anyone can inspect frontend code or attempt a URL. The platform remains safe only when admin APIs require admin authority and supplier APIs enforce the supplier's scope.

The frontend boundary answers: which product should this user experience? The backend boundary answers: which operations and records is this identity authorised to use?

Keeping those responsibilities separate lets the team optimise delivery without weakening marketplace isolation.

Inject configuration where the Cloud build actually runs

A local dashboard Vite configuration is not necessarily the configuration used in production.

Medusa Cloud initiates the admin build from the backend. Variables, aliases and compile-time definitions required by the custom panel must therefore be supplied through the Medusa admin Vite hook, where that build can see them.

The project defines the application base, backend relationship, storefront URL, environment label and selected integration keys there. It also points static assets at the dashboard's public directory and lists dependencies the build should pre-optimise.

Assets follow the same rule. Because Medusa serves the dashboard beneath /app in the shared setup, the favicon is resolved from the compiled base rather than assuming /favicon.ico at the origin root.

Configuration belongs to the system that performs the production build, not merely the tool developers run locally.

Why this architecture suited Medusa Cloud

The marketplace needed custom operational products, but it did not need a second commerce runtime.

Medusa's dashboard build hook made the backend deployment an effective shell for those products. React and Vite provided separate application entry branches, routers and shared components. Hostname routing made each panel feel independent to its audience.

The architecture reduced deployment surface while preserving product clarity. Backend and dashboard contracts move together. Shared design foundations stay close. Operators and suppliers receive URLs and navigation designed for their work.

There is another practical benefit: preview and test environments inherit the same relationship. A branch does not need two independently configured frontend previews to exercise a backend capability across both audiences. The release candidate contains the admin and supplier experiences expected by that exact server version. That does not remove the need to test both products, but it makes the unit under test explicit and reproducible.

This is the architectural choice: consolidate what is operationally shared, separate what is experientially and securely distinct.

A two-panel delivery checklist

Before combining operational panels into one build, ask:

  1. Are the audiences doing genuinely different jobs?
  2. Does each product own a separate route tree?
  3. What trusted signal selects the application?
  4. Does each dedicated host receive a clean root basename?
  5. Which legacy paths must remain valid?
  6. Are deep-link paths and query strings preserved during migration?
  7. Is only the selected application initialised?
  8. Which components are truly shared concepts?
  9. Where does the production Vite build receive its variables?
  10. Does the public asset base match how Medusa serves the dashboard?
  11. Can backend-generated links target the right public panel?
  12. Are admin and supplier permissions enforced server-side?
  13. Can one release update backend and panels coherently?
  14. Is the fallback for an unknown host or path explicit?

The answer should reduce operating burden without blurring product or security boundaries.

The broader lesson

Two products do not always require two deployment platforms.

For this marketplace, we built a shared Medusa Cloud dashboard delivery that selects a dedicated internal or supplier application by hostname, keeps clean roots on the new domains and preserves legacy paths for existing links.

Each audience receives a focused product. The engineering team retains one build and release unit. Shared foundations remain reusable. Backend authorization remains authoritative.

That is the balance worth designing for: fewer moving parts for the team running the platform, with no ambiguity for the people using it.

Read next
Keep the useful ideas coming

One practical commerce field note at a time.

Join the WeAreSouk journal for grounded stories about Medusa, Shopify, AI, integrations and the systems behind serious commerce.

Working on a similar problem?Bring us the business constraint. We’ll help map the system behind it.Talk to Souk →
Souk AI · online now

Turn the article into an implementation plan.

Ask how this applies to your store, your stack, or your current bottleneck.

01 Describe your current setup.02 Name the workflow or signal that feels unreliable.03 Get a practical first architecture back.
I can help map this article to your stack. Tell me what you sell, what platform you use, and where the medusa & architecture question hurts.
Continue the cluster

Building a property procurement platform on Medusa

Start a conversation

Tell us what commerce needs to do for your business.

No scheduling maze. Send the context, the constraint or the idea. We will read it and come back to you directly.