Skip to main content

Layout components

Layout components allow you to customize the visual appearance and structure of the K3 configurator. You can use higher-order components (HOCs) to extend or completely replace existing components.

Higher-Order Components (HOCs)​

A HOC is a function that takes a React component and returns a new, extended component:

__PROTECTED_0__

const myHOC: HOC = (OriginalComponent) => {
return (props) => {
// Eigene Logik vor dem Rendering
console.log("Rendering component with props:", props);

// Original-Komponente mit modifizierten oder zusätzlichen Props rendern
return (
<div className="my-wrapper">
<OriginalComponent {...props} customProp="value" />
</div>
);
};
};

Basic structure​

Layout extensions are registered in the ui.layout object:

// In Plugin.tsx
export default {
// ...
ui: {
layout: {
header: {
hoc: myHeaderHOC,
description: "Erweitert die Kopfzeile mit einem Custom Badge"
},
sidebar: {
hoc: mySidebarHOC,
description: "Fügt zusätzliche Navigation hinzu"
}
}
}
}

Each layout slot requires:

  • hoc: The HOC function
  • description: Description of the extension point (string or localized string)

Available layout slots​

root​

Overwrites the topmost wrapper component of the entire application. Wraps everything - header, sidebar, content and footer.

Application examples:

  • Global theme providers
  • Error Boundaries
  • App-wide contexts
root: {
hoc: (Original) => (props) => (
<MyThemeProvider>
<Original {...props} />
</MyThemeProvider>
),
description: "Fügt einen globalen Theme-Provider hinzu"
}

Overwrites the header of the configurator (header bar).

Typically contains:

  • Logo/Branding
  • Navigation buttons
  • Scene buttons
  • Price display
header: {
hoc: (Original) => (props) => (
<>
<div className="custom-banner">Sonderaktion!</div>
<Original {...props} />
</>
),
description: "Fügt einen Banner über dem Header hinzu"
}

Overwrites the entire sidebar, which contains the groups and variables.

sidebar: {
hoc: (Original) => (props) => (
<div className="custom-sidebar-wrapper">
<Original {...props} />
<div className="sidebar-footer">Zusätzliche Infos</div>
</div>
),
description: "Erweitert die Sidebar mit Footer"
}

sidebarHeader​

Overwrites the header area of the sidebar (above the group list).

sidebarFooter​

Overwrites the footer area of the sidebar (below the group list).

Overwrites the footer of the configurator.

contentView​

Overwrites the main content area (3D viewer, gallery etc.).

Overwrites the gallery view for image configurators.

branding​

Overwrites the branding component (logo + brand name).

branding: {
hoc: (Original) => (props) => (
<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
<Original {...props} />
<span className="beta-badge">BETA</span>
</div>
),
description: "Fügt ein Beta-Badge neben dem Logo hinzu"
}

Overwrites only the logo element within the branding.

Overwrites the navigation buttons (back/forward between groups).

exitButtons​

Overwrites the exit buttons.

sceneButtons​

Overwrites the scene buttons (AR, screenshot, fullscreen etc.).

price​

Overwrites the price display.

price: {
hoc: (Original) => (props) => (
<div className="custom-price-wrapper">
<span className="price-label">Ihr Preis:</span>
<Original {...props} />
<span className="price-vat">inkl. MwSt.</span>
</div>
),
description: "Erweitert Preisanzeige mit Label und MwSt-Hinweis"
}

labelActionDisplay​

Overwrites the label/hotspot display on the desktop 3D canvas.

mobileLabelActionDisplay​

Overwrites the label/hotspot display on mobile devices.

invalidRuleModal​

Overwrites the modal that is displayed for invalid rule constellations.

mountedWhenLoaded​

A component that is only mounted after the configurator has been fully loaded.

Application examples:

  • Analytics tracking
  • Custom loading completion handler
mountedWhenLoaded: {
hoc: (Original) => (props) => {
useEffect(() => {
console.log("Configurator fully loaded!");
// Analytics Event senden
}, []);
return <Original {...props} />;
},
description: "Führt Code nach vollständigem Laden aus"
}

configurator​

Overwrites the main configurator component (group navigation + variable area).

groupLabel​

Overwrites the group label in the sidebar navigation.

groupLabel: {
hoc: (Original) => (props) => (
<div className="custom-group-label">
<span className="group-icon">📁</span>
<Original {...props} />
</div>
),
description: "Fügt Icons vor Gruppennamen hinzu"
}

groupPanel​

Overwrites the expandable group panel that contains the variables.

additionalGroups​

Enables additional groups to be added at the end of the group list.

additionalGroups: {
hoc: (Original) => (props) => (
<>
<Original {...props} />
<div className="custom-group">
<h3>Zusätzliche Optionen</h3>
{/* Custom Content */}
</div>
</>
),
description: "Fügt eine zusätzliche Gruppe hinzu"
}

additionalVars​

Enables additional variables to be added at the end of the variable list of a group.

variableLabel​

Overwrites the label of each individual variable in the sidebar.

Application examples:

  • Custom tooltip icons
  • Mandatory field markers (*)
  • Accessibility additions
variableLabel: {
hoc: (Original) => (props) => (
<div style={{ display: "flex", alignItems: "center" }}>
<Original {...props} />
{props.required && <span className="required">*</span>}
</div>
),
description: "Fügt Pflichtfeld-Markierung hinzu"
}

Localized descriptions​

Descriptions can be multilingual:

__PROTECTED_1__

header: {
hoc: myHeaderHOC,
description: {
de: "Überschreibt die Kopfzeile",
en: "Overrides the header bar",
fr: "Remplace la barre d'en-tête",
nl: "Overschrijft de headerbalk"
} as LocalizedString
}

Best Practices​

1. always render the original component​

If you do not want to replace the entire component, always render the original component:

hoc: (Original) => (props) => (
<>
{/* Ihr Custom Content */}
<Original {...props} />
</>
)

2. do not mutate props​

If necessary, create new props objects instead of changing the existing ones:

hoc: (Original) => (props) => {
const enhancedProps = { ...props, customValue: "added" };
return <Original {...enhancedProps} />;
}

3. pay attention to performance​

HOCs are called with every render. Avoid expensive calculations:

hoc: (Original) => {
// ❌ Schlecht: Berechnung bei jedem Render
return (props) => {
const expensiveValue = computeExpensive();
return <Original {...props} extra={expensiveValue} />;
};

// ✅ Gut: Memoization verwenden
return (props) => {
const expensiveValue = useMemo(() => computeExpensive(), []);
return <Original {...props} extra={expensiveValue} />;
};
}

4. use TypeScript types​

Use the provided TypeScript types for better IDE support:

__PROTECTED_2__

const myHOC: HOC = (Original) => (props) => <Original {...props} />;

const layoutExtensions: K3LayoutExtensions = {
header: {
hoc: myHOC,
description: "My header extension"
}
};

Complete example​

__PROTECTED_3__

// Root Theme Wrapper
const rootHOC: HOC = (Original) => (props) => (
<div className="custom-theme">
<Original {...props} />
</div>
);

// Logo mit Badge
const logoHOC: HOC = (Original) => (props) => (
<div style={{ position: "relative" }}>
<Original {...props} />
<span className="badge">NEU</span>
</div>
);

// Erweiterte Preisanzeige
const priceHOC: HOC = (Original) => (props) => (
<div className="enhanced-price">
<div className="price-main">
<Original {...props} />
</div>
<div className="price-disclaimer">
* Alle Preise inkl. MwSt.
</div>
</div>
);

// Im Plugin registrieren:
export default {
// ...
ui: {
layout: {
root: {
hoc: rootHOC,
description: "Fügt Custom Theme Wrapper hinzu"
},
logo: {
hoc: logoHOC,
description: {
de: "Fügt Badge neben dem Logo hinzu",
en: "Adds badge next to logo"
}
},
price: {
hoc: priceHOC,
description: "Erweiterte Preisanzeige mit Disclaimer"
}
}
}
};