Vue Storefront is now Alokai! Learn More
Sub-path Routing Implementation

Sub-path Routing Implementation

This guide provides step-by-step instructions for implementing sub-path routing in your Alokai application.

Prerequisites

Before starting, ensure you've read the overview and made your architectural decision using the choosing approach guide.

Prerequisites

Before implementing sub-path routing, you need to configure your Middleware to support different configurations using the Config Switcher extension.

Config Switcher Setup Required

Follow the complete Config Switcher installation guide to set up your middleware with multiple configurations. For sub-path routing, you'll want to configure different settings for each path segment (e.g., apparel, electronics) in your Config Switcher setup.

Setting Up the Storefront

Configure your Storefront to send the appropriate config ID to the Middleware based on the current URL path.

In your Next.js SDK utils, implement the getConfigSwitcherHeader function:

// apps/storefront-unified-nextjs/sdk/modules/utils.ts
import { defineGetConfigSwitcherHeader } from '@vue-storefront/next';

export const defaultConfigId = 'apparel';
export const configIds = ['apparel', 'electronics'];

export const getConfigSwitcherHeader = defineGetConfigSwitcherHeader(
  ({ pathname }) => configIds.find((configId) => pathname.startsWith(`/${configId}`)) ?? defaultConfigId,
);

Updating the Routing Strategy

To enable path-based routing, you need to restructure your application to include a dynamic [configId] parameter in the route. You can choose one of two strategies:

  • [configId] as second parameter - /de/electronics/category (Recommended)
  • [configId] as first parameter - /electronics/de/category

For Next.js apps using the App Router, update your file structure:

apps/storefront-unified-nextjs/app/
├── [locale]/                 # Locale parameter
│   ├── [configId]/           # Store identifier parameter
│   │   ├── (default)/        # Routes for all stores
│   │   │   ├── page.tsx      # Home page
│   │   │   ├── cart/         # Cart pages
│   │   │   ├── checkout/     # Checkout pages
│   │   │   └── products/     # Product pages
│   │   ├── (electronics)/    # Electronics-specific routes
│   │   │   └── some-page/    # Page name
│   │   ├── (apparel)/        # Apparel-specific routes
│   │   │   └── another-page/ # Apparel-specific home
│   │   └── layout.tsx        # Apply store-specific class
│   ├── favicon.ico

[configId] as First Path Parameter

This approach places the configId as the first parameter in the URL structure. Note that this impacts i18n packages and requires additional configuration changes to maintain proper internationalization.

Not Recommended

This approach is not recommended as it impacts the i18n packages and requires additional configuration changes to maintain proper internationalization. Consider using the second path parameter approach unless you have specific requirements that necessitate this structure. If you really need such option, contact our support.

This structure enables routes like:

  • /en/electronics - Electronics store home
  • /en/apparel - Apparel store home
  • /en/electronics/category - PLP in the electronics store
  • /en/apparel/category - PLP in the apparel store

Update Internal Links

After setting up the path routing structure, you must update all internal links in your application to include the appropriate configId parameter. This is crucial to maintain proper navigation within each store and prevent users from accidentally switching between stores when clicking links.

Make sure that all your internal links and navigation components preserve the store context by including the current configId in the URLs.

Store-Specific Styling

Implement configuration-specific styling through CSS variables and Tailwind variants:

1. Applying Store Identifier Class

Apply the configId as a CSS class to enable different styles for each configuration:

In your Next.js layout, apply the configId as a class to the root layout element:

// apps/storefront-unified-nextjs/app/[locale]/[configId]/layout.tsx
import { PropsWithChildren } from 'react';

interface LayoutProps extends PropsWithChildren {
  params: {
    configId: string;
  };
}

export default function Layout({ children, params: { configId } }: LayoutProps) {
  return (
    <div className={configId}>
      {children}
    </div>
  );
}

2. Defining Store-Specific CSS Variables

Define CSS variables that change based on the store:

// apps/storefront-unified-nextjs/app/[locale]/globals.scss
@tailwind base;
@tailwind components;
@tailwind utilities;

// Electronics store theme
.electronics {
  --colors-primary-50: 45 249 255;
  --colors-primary-100: 233 243 255;
  --colors-primary-500: 51 117 255;
  // ... other variables
}

// Apparel store theme (different color scheme)
.apparel {
  --colors-primary-50: 243 254 249;
  --colors-primary-100: 224 247 235;
  --colors-primary-500: 45 165 116;
  // ... other variables
}

3. Using Store-Specific Styles

Update your Tailwind config and use variants in your components:

// packages-end-user/tailwind-config/src/nextjs.ts
import plugin from "tailwindcss/plugin";

const config: Config = {
  plugins: [
    plugin(({ addVariant }) => {
      addVariant("electronics", ".electronics &");
      addVariant("apparel", ".apparel &");
    }),
  ],
};

Then use these variants in your components:

export default function NavbarTop({ children, className }: NavbarTopProps) {
  return (
    <header
      className={classNames(
        'sticky top-0 z-40 flex h-14 bg-primary-800',
        'electronics:!bg-primary-600',
        'apparel:!bg-primary-400',
        'md:-top-5 md:h-20 md:pt-2.5',
        className
      )}
    >
      {children}
    </header>
  );
}

Conditional Components

Handle store-specific components using two main approaches:

Approach 1: Different Versions of the Same Component

Useful when you need different variations of a component based on the store:

// apps/storefront-unified-nextjs/components/announcement-bar.tsx
import { ApparelAnnouncementBar } from '@/components/store-apparel/announcement-bar';
import { ElectronicsAnnouncementBar } from '@/components/store-electronics/announcement-bar';

interface AnnouncementBarProps {
  configId: string;
}

function AnnouncementBar({ configId }: AnnouncementBarProps) {
  const StoreComponent = {
    apparel: ApparelAnnouncementBar,
    electronics: ElectronicsAnnouncementBar,
  }[configId];

  return StoreComponent ? <StoreComponent /> : null;
}

Approach 2: Conditional Rendering Based on Store

Useful when certain components should only appear in specific stores:

export default function HomePage({ params: { configId } }: HomePageProps) {
  return (
    <div>
      <h1>Welcome to our store</h1>
      
      {configId === 'electronics' && (
        <ElectronicsPromo />
      )}
      
      <div>
        {/* Common content for all stores */}
      </div>
    </div>
  );
}

Creating Store-Specific Pages

Some pages might only exist for specific stores:

For Next.js, use route groups with the store name, and check the configId parameter:

// apps/storefront-unified-nextjs/app/[locale]/[configId]/(electronics)/layout.tsx
import { notFound } from 'next/navigation';

export default function ElectronicsLayout({ children, params }: ElectronicsLayoutProps) {
  if (params.configId !== 'electronics') {
    notFound();
  }

  return <div>{children}</div>;
}

Summary

Sub-path routing implementation involves:

  1. Config Switcher Setup: Configure your middleware with multiple integration configurations
  2. Storefront Configuration: Set up header functions to send the appropriate config ID
  3. Routing Structure: Implement dynamic [configId] parameters in your routes
  4. Styling System: Use CSS variables and Tailwind variants for configuration-specific styles
  5. Conditional Logic: Implement component and page variations based on the active configuration

This approach provides a powerful single-deployment solution for serving multiple configurations while maintaining unified user experience and shared functionality.