Skip to content

RFC: Vendure Storefront UI / Starter #3675

@HouseinIsProgramming

Description

@HouseinIsProgramming

Background

We are building a Vendure storefront that is both flexible and developer-friendly.

Our goal is to provide a solid foundation that developers can easily build upon, while drastically reducing the starting friction and the time it takes to go from concept to minimum viable product (MVP).

This would also provide a fully functional demo that can be installed using 1 create command.

But to do that, we need to make the right choices about how we distribute the UI components.

The goal is to ensure we’re building something that works for both developers who want simplicity and provide reasonable flexibility when it comes to customization.

With our current proposals that would have certain trade-offs and ultimately result in either something that is requires less maintenance overhead but is more opinionated and would require more effort while customizing components, or something that will require more maintenance but wouldn’t force you into any “defaults”.

Proposals:

We have two options that we that we would like to propose.

Both of these approaches would include an SDK, similar to the one proposed by @michaelbromley in this issue: #2621

The component distribution is where these 2 approaches differ.

Option 1: NPM Package Distribution

Traditional npm package that developers install and import.

This is the more opinionated and lower maintenance approach.

Pros:

  • Familiar workflow - standard npm install
  • Automatic updates - bug fixes delivered through package updates
  • Smaller bundle size - external dependencies
  • Version management with semantic versioning

Cons:

  • Less customization - harder to modify internals
  • Dependency management overhead
  • Breaking changes require migration
  • May need separate packages for different frameworks
  • Component internals "hidden" from developers

Example Workflow

1. Install the Starter Storefront

Create a new Vite-based storefront that includes the pre-built components and hooks:

npx @vendure/create my-shop --with-vite-storefront
cd my-shop
npm run dev

File structure:

my-shop 
└─ apps
	└─ vendure
	└─ storefront/
		├── package.json       # @vendure/storefront-ui dependency
		├── src/
		│   ├── pages/         # Uses imported components, custom or prebuilt
		│   ├── components/    # Only custom components - made by user
		│   └── lib/
		└── node_modules/
		    └── @vendure/storefront-ui/  # Pre-built components live here

2. Modify Components

Since the components are imported from the NPM package, customization is limited to props or Tailwind classes.

// src/pages/all-products.tsx

const products = // fetch from Vendure
const page = 1

function handlePageChange(page: string) {
  // refetch products with new page
}

// all of the props would have to be "hard-coded" in the NPM component package
<ProductGrid page={page} onPageChange={(page) => handlePageChange(page)}>
  {products.map((product) => (
    <ProductCard product={product} />
  ))}
</ProductGrid>;

3. Update Components

To get the latest updates or bug fixes, update the NPM package:

npm update @vendure/storefront-ui

# or update your package.json

This would require no further maintenance, unless it would be a version with breaking changes.

4. No Internal Modifications

Developers cannot modify the internal logic or structure of the components. They can only use the exposed props and Tailwind classes for customization.


Option 2: shadcn Registry Distribution

Components distributed through shadcn-style registry - copies code directly into your project.

This would require more maintenance but offers infinite customizability of already built components.

Pros:

  • Full customization - complete source code access
  • No external dependencies - components in your codebase
  • Framework agnostic - same components work across React-based meta frameworks
  • Familiar to developers - shadcn/ui has widespread adoption
  • Gradual adoption - install only needed components
  • No version conflicts

Cons:

  • Manual updates - developers must sync improvements manually
  • Maintenance burden on developers
  • Code duplication across projects
  • Consistency challenges

Example Workflow

1. Install the Starter Storefront

Create a new Vite-based storefront that includes the components copied from the shadcn registry:

npx @vendure/create my-shop --with-vite-storefront
cd my-shop
npm run dev

File structure:

my-shop 
└─ apps
	└─ vendure
	└─ storefront/
			├── package.json             # No vendure *component* dependencies
			└── src/
			    ├── pages/               # Uses local components
			    ├── components/
			    │   └── vendure/         # Vendure components (copied from registry)
			    ├── hooks/               # All hooks (copied from registry)
			    └── lib/

2. Modify Components (Full Control)

Since the components are copied directly into the project, developers have full access to the source code and can modify them as needed.

Example: Adding pagination functionality
A developer can open the ProductGrid component file and add new props and UI elements for pagination. This level of direct modification is the key benefit of this approach.

// src/components/vendure/product-grid.tsx
export function ProductGrid({
  children,
  page,
  onPageChange,
}: ProductGridParams) {
  return (
    <div>
      {/* The developer has complete freedom to change the component. */}
      <div className="grid grid-cols-3 gap-6">{children}</div>

      {/* They can add pagination controls directly into the component */}
      <div className="mt-4 flex justify-center gap-2">
        <button onClick={() => onPageChange(page - 1)}>Previous</button>
        <span>Page {page}</span>
        <button onClick={() => onPageChange(page + 1)}>Next</button>
      </div>
    </div>
  );
}

This modified component is then used on the page. The usage looks identical to the NPM example, but the components are now local and not part of the node_modules and fully controlled by the developer.

// src/pages/all-products.tsx
const products = []; // fetch from Vendure
const page = 1

function handlePageChange(page: number) {
  // refetch products with new page
}

<ProductGrid page={page} onPageChange={(page) => handlePageChange(page)}>
  {products.map((product) => (
    <ProductCard product={product} />
  ))}
</ProductGrid>;

3. Manual Updates

To sync updates from the registry, re-install the component:

npx shadcn@latest add http://storefront-registry.vendure.io/r/product-grid.json

This would completely overwrite your components, then you would have to compare the updated components with the git diff of what was overwritten and re-add your custom code.

4. Full-access to customization

Developers have full control over the component internals, including logic, structure, and styling. They can rewrite or extend components as needed.


Feedback questions:

  1. Which distribution method would you prefer?
    • NPM package for easy installation and updates.
    • shadcn registry for full customization control but a bit more involved updating process.
  2. What level of customization do you typically need?
    • Basic theming and styling
    • Component behavior modifications
    • Complete component rewrites
  3. How important are automatic updates vs. stability?

Metadata

Metadata

Assignees

No one assigned

    Labels

    design 📐This issue deals with high-level design of a featuredx 💙Quality of life improvements for developers

    Type

    No type

    Projects

    Status

    📦 Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions