Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

docs(tf-414): different markups for components #7413

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,254 @@ navigation:
icon: tabler:number-1-small
---

# Different markups for individual components
# Different Markups for Individual Components

## Prerequisites

Familiarity with [Tooling & Concepts → File-Based Inheritance](/guides/multistore/tooling-and-concepts/file-based-inheritance) is required to understand file override mechanisms and inheritance principles.

**What You’ll Learn**

::list{type="success"}
- How to safely modify the markup of individual components in a multibrand project without breaking their props interfaces
- How to handle breaking changes in component props with step-by-step workflows, including duplicating and modifying files
- Best practices for managing component markup customization to maintain consistency and avoid duplication
- How to implement these changes using examples in Next.js and Nuxt to adapt to specific frameworks
::

## Why Customize Component Markups?

In multibrand setups, different stores often require unique layouts or styling. Customization is necessary to ensure proper alignment with branding and design guidelines while sharing common functionalities across stores. For example:

- Store-specific customizations to match unique branding designs.
- Adjustments to the structure or layout of a component in one brand without affecting others.

Changing a component’s markup is a **structured process**, and this guide will walk you through **safe modifications** and scenarios where **breaking changes** are unavoidable.

## Safe Markup Changes Without Breaking Props

The most straightforward way to customize a component is by modifying **only its markup** while keeping its props interface unchanged. This ensures compatibility across the codebase and minimizes any disruption to dependent components or features.

### When to Use

- To rearrange the component structure.
- To adjust styling for specific stores or brands without altering the functionality.

### The Solution: Component Override

In cases where a specific store requires customization, you can override the component by duplicating and modifying it within that store's directory.

#### Example Workflow

1. **Duplicate** the component inside the store-specific directory.
2. **Modify** the duplicated component as per the store's requirements (e.g., layout, structure, or styles).

#### In Next.js

Here’s how to override `ExampleComponent` for a specific store (`store-a`) in a Next.js project:

```tsx
// /apps/stores/store-a/storefront-unified-nextjs/components/example-component.tsx

// Props remain unchanged
interface ExampleComponentProps {
title: string;
imageSrc: string;
}

export default function ExampleComponent({ title, imageSrc }: ExampleComponentProps) {
return (
<div className="flex items-center w-md"> {/* Customized className for store-a */}
<img src={imageSrc} alt={title} />
<h2>{title}</h2>
{/* Customized content for store-a */}
</div>
);
}
```

#### In Nuxt

The process is similar in Nuxt. Here’s an example:

```vue
// /apps/stores/store-a/storefront-unified-nuxt/components/ExampleComponent/ExampleComponent.vue

<template>
<!-- Customized className for store-a -->
<div class="flex items-center w-md">
<img :src="imageSrc" :alt="title" />
<h2>{{ title }}</h2>
<!-- Customized content for store-a -->
</div>
</template>

<script setup lang="ts">
defineProps<{
title: string;
imageSrc: string;
}>();
</script>
```

### Outcome

In both cases (Next.js and Nuxt), props remain untouched, ensuring compatibility with the rest of the application. Adjustments remain restricted to layout or presentation, avoiding unnecessary duplication or breaking changes to functionality.

## Breaking Changes in Component Markup

Some changes may require adjustments to the component’s **props interface**, meaning that the component is no longer compatible with its previous usage. These are referred to as **breaking changes** because they can potentially impact other parts of the application if not handled correctly.

### When to Use

- When introducing additional props for brand-specific requirements.
- When removing unused or deprecated props for cleaner, leaner implementations.
- When creating a fundamentally different component design unique to specific brands or stores.

### Example Workflow for Breaking Changes

#### Scenario 1: Adding Props

The `ExampleComponent` requires a new prop `customSubtitle`, exclusively for `store-b`.

1. **Duplicate** the component for `store-b`.
2. **Modify the props interface** to include the new or modified prop.
3. **Implement fallback behaviors** to ensure existing components using the old interface remain functional, or document the change clearly to propagate updates across the codebase.

#### In Next.js

```tsx
// /apps/stores/store-b/storefront-unified-nextjs/components/example-component.tsx

interface ExampleComponentProps {
title: string;
imageSrc: string;
customSubtitle?: string; // New optional prop
}

export default function ExampleComponent({ title, imageSrc, customSubtitle }: ExampleComponentProps) {
return (
<div className="store-b example-component">
<img src={imageSrc} alt={title} />
<h2>{title}</h2>
{customSubtitle && <p className="subtitle">{customSubtitle}</p>} {/* Customized subtitle */}
</div>
);
}
```

#### In Nuxt

```vue
// /apps/stores/store-b/storefront-unified-nuxt/components/ExampleComponent/ExampleComponent.vue

<template>
<div class="store-b example-component">
<img :src="imageSrc" :alt="title" />
<h2>{{ title }}</h2>
<!-- Customized subtitle for store-b -->
<p v-if="customSubtitle" class="subtitle">{{ customSubtitle }}</p>
</div>
</template>

<script setup lang="ts">
defineProps<{
title: string;
imageSrc: string;
customSubtitle?: string; // New optional prop
}>();
</script>
```

#### Scenario 2: Removing Props

In this scenario, we'll demonstrate removing the `imageSrc` prop because the component no longer displays an image.

1. **Identify Components That Need Changes**: Determine which components need the prop removed.
2. **Create a Plan for Migration**: Decide how to handle existing usages of the removed prop.
3. **Update Component and Its Usages**: Remove the prop and update all component instances.

#### In Next.js

```tsx
// /apps/stores/store-a/storefront-unified-nextjs/components/example-component.tsx

interface ExampleComponentProps {
title: string;
// imageSrc prop removed
}

export default function ExampleComponent({ title }: ExampleComponentProps) {
return (
<div>
{/* Image element removed */}
<h2>{title}</h2>
</div>
);
}
```

#### In Nuxt

```vue
// /apps/store-a/storefront-unified-nuxt/components/ExampleComponent/ExampleComponent.vue

<template>
<div>
<!-- Image element removed -->
<h2>{{ title }}</h2>
</div>
</template>

<script setup lang="ts">
defineProps<{
title: string;
// imageSrc prop removed
}>();
</script>
```

### Handling the Migration

When removing props like `imageSrc`, consider these strategies:

1. **Communicate Changes**: Notify all teams about the breaking change.
2. **Use TypeScript**: Let TypeScript help identify places where the removed prop is still being used.
3. **Implement Gradually**: For large codebases, consider making the prop optional first before removing it completely.
4. **Documentation**: Update component documentation to reflect the new props interface.

This approach ensures a smooth transition when removing props from components while minimizing disruption across your multibrand project.


### Testing and Verifying Breaking Changes

While introducing breaking changes, follow these strategies to ensure stability:

1. **Test Locally:**
- Verify that dependent stores or components remain functional.
- Use `yarn dev` to observe changes in real-time.

2. **Document Changes Clearly:**
- Update relevant guides or internal documentation pointing out the new props and their usage.

3. **Run Integration Tests:**
- Use `yarn test:integration:pw` to validate functionality across the inheritance chain.

4. **Communicate with Teams:**
- Breaking changes should be coordinated with development teams to avoid unnecessary disruptions.

## Best Practices for Customizing Component Markups

#### Do:
- Keep props interfaces intact when possible.
- Leverage duplication and overrides for brand-specific customizations.
- Document all breaking changes thoroughly.

#### Avoid:
- Excessive overrides that increase maintenance costs.
- Introducing breaking changes without backward compatibility.
- Skipping tests after applying updates.

## Conclusion

Customizing individual components in a **multibrand setup** can effectively address brand-specific requirements while maintaining maintainable code. Follow these steps to confidently adjust component markups while avoiding breaking changes whenever possible.
Loading