Configuration Data Model
When you work with Mimeeq's observers, actions, or events, you're interacting with a layered data structure that represents how a product's configuration options are organized. Understanding this hierarchy is essential for building custom UIs, consuming configuration data, or debugging why something appears the way it does.
This article explains each layer from the bottom up: what it is, what data it carries, and how you access it.
The Hierarchy
Mimeeq organizes product configuration in up to four levels:
Container (optional) ← High-level category, custom UI only
└─ Group ← Organizational section in the option panel
└─ Block ← A single configurable aspect (e.g., "Frame Color")
└─ Options ← The choices available (sourced from Option Sets)
Not every product uses all levels. A simple product might have a few blocks in a single group with no containers. A complex product like a configurable vehicle might use all four levels.
Options and Option Sets
Options are the individual choices a user can make — "Oak", "Walnut", "Birch" for a material block, or "Red", "Blue", "Green" for a color block.
Options live inside Option Sets, which are reusable collections managed in the Mimeeq Admin Panel. A single option set can be shared across multiple products, so updating it in one place updates all products that reference it. Option sets can also be organized into Material Collections, which group related option sets together (e.g., a "2024 Premium Fabrics" collection containing multiple fabric option sets).
Each option carries data you'll encounter in observers and event payloads:
| Property | Description |
|---|---|
name | Display name shown to users |
code | Configuration code used in the configuration string |
priceCode | Code used for pricing calculations |
value | Internal value |
imageSrc | Path to option image (thumbnails, swatches) |
thumbnail | Absolute URL to the 425px thumbnail |
optionSetId | Which option set this option belongs to |
hideInPdf | Whether to exclude from PDF exports |
hideInOptionPanel | Whether to hide in the configurator UI |
metafields | Custom parameters attached to this option |
For special widgets (text, number, slider, color, engrave, image, print on demand), option data is structured differently. See Widget Types below.
Blocks
A Block represents a single configurable aspect of the product — "Frame Color", "Seat Material", "Width", "Engraving Text". It's the core unit of the configuration panel.
Each block has a widget type that determines how its options are presented to the user, and a mode that determines where its options come from.
You access blocks through the optionSets.blocks observer:
window.mimeeqApp.observers.optionSets.blocks.subscribe(({ newValue }) => {
if (newValue) {
newValue.forEach(block => {
console.log(block.name); // "Frame Color"
console.log(block.blockName); // "FrameColor" (identifier used in code)
console.log(block.widgetType); // "THUMBNAILS"
console.log(block.options); // Array of available options
console.log(block.optionsLength); // Number of options
});
}
});
Key block properties
| Property | Description |
|---|---|
id | Unique identifier |
blockName | Name used in code, configuration codes, and the markOptionByBlockNameAndOptionCode action |
name | Localized display name |
widgetType | How options are rendered — see Widget Types |
mode | How options are sourced — LINKED, MANUAL, ENGRAVE, IMAGE, PRINT_ON_DEMAND, INPUT, COLOR, SLIDER, BUTTON, MESSAGE |
options | Array of available OptionSetOption objects |
optionSetIds | IDs of option sets attached to this block |
letterMappings | Maps option set IDs to letters used in configuration code encoding |
clearable | Whether the user can deselect (return to empty) |
isDisabled | Whether the block is currently disabled (e.g., by rules) |
isHidden | Whether the block is hidden (e.g., by rules) |
thumbnail | Block image, available for custom UIs |
The blockName is what you pass to markOptionByBlockNameAndOptionCode. It's case-sensitive and matches the name set in the admin panel.
Block modes
The mode property tells you where the block's options come from:
| Mode | Description |
|---|---|
LINKED | Uses existing Option Sets or Material Collections. Most common mode. |
MANUAL | Uses option sets created locally for this specific block. |
ENGRAVE | Text engraving/embossing input. |
IMAGE | User image upload (logos, textures). |
PRINT_ON_DEMAND | Advanced image placement on print areas. |
INPUT | Text or number input field. |
COLOR | Color picker widget. |
SLIDER | Numeric slider. |
BUTTON | Interactive button that triggers rules. Does not store a value. |
MESSAGE | Display-only informational content. Does not store a value. |
For LINKED and MANUAL modes, the block's type property further specifies whether options come from a MATERIAL_COLLECTION or an OPTION_SET.
Widget Types
The widgetType on a block controls how options are rendered in the standard UI. When building a custom UI, you'll use this value to decide which component to render for each block.
Selection widgets
These present a list of options for the user to pick from:
| Widget Type | Description |
|---|---|
RADIO_PLAIN_TEXT | Vertical radio button list with text labels |
NUMBERS_BUTTON | Row of clickable buttons for numbers or short text |
NUMBERS_SCALE | Scale/range selector for numeric values |
SELECT_LISTBOX | Scrollable listbox (dropdown-like) |
SELECT_RADIO | Dropdown with radio selection |
THUMBNAILS | Grid of image thumbnails |
THUMBNAILS_GROUPS | Thumbnails with a dropdown to switch between option sets |
FILTER | Filterable list with attribute-based narrowing |
FILTER_GROUPS | Filterable list grouped by attribute categories |
MULTICHOICE | Multiple selections allowed (checkboxes) |
Input widgets
These capture direct user input rather than a selection from predefined options:
| Widget Type | Description |
|---|---|
TEXT | Single-line text input |
NUMBER | Numeric input with validation |
SLIDER | Numeric slider with min/max range |
ENGRAVE | Multi-line text with font and formatting options |
COLOR | Color picker (hex value) |
IMAGE | Image/texture upload |
PRINT_ON_DEMAND | Image upload with positioning on print areas |
Non-value widgets
These don't contribute to the configuration code:
| Widget Type | Description |
|---|---|
BUTTON | Triggers rules when clicked. Omitted from configuration codes. |
MESSAGE | Displays informational content. Omitted from configuration codes. |
When building a custom UI, the widgetType is your switch statement. Map each type to your own component, and use the block's options, inputSettings, engraveSettings, colorSettings, or printOnDemandSettings depending on which widget you're rendering.
Groups
A Group organizes related blocks into a named section. In the standard Mimeeq UI, groups appear as collapsible sections in the option panel. A product always has at least one group.
You access groups through the optionSets.blockGroups observer, which provides groups enriched with selection status:
window.mimeeqApp.observers.optionSets.blockGroups.subscribe(({ newValue }) => {
if (newValue) {
newValue.forEach(group => {
console.log(group.name); // "Upholstery"
console.log(group.hasErrors); // true if required selections are missing
console.log(group.values); // Array of { name, value } for selected blocks
});
}
});
Key group properties
| Property | Description |
|---|---|
id | Unique identifier |
name | Localized display name |
optionSetIds | IDs of blocks that belong to this group |
selectMatching | If true, selecting an option in one block will auto-select matching options in other blocks of the same group (e.g., matching fabric across seat and back) |
hasErrors | true if any block in this group has missing required selections |
values | Array of BlockSelectedValue — the current selections for each visible block |
hideOnSpecification | If true, hide this group from PDF specs and summaries |
preview | Optional preview image settings for the group |
The hasErrors flag is your go-to for building validation indicators. When true, at least one block in the group needs attention.
Containers
A Container adds an optional higher-level grouping above groups. This is purely organizational and exists specifically for custom UI implementations — the standard Mimeeq configurator UI does not render containers.
If a product has containers defined, they appear in the optionSets.blockGroupContainers observer:
window.mimeeqApp.observers.optionSets.blockGroupContainers.subscribe(({ newValue }) => {
if (newValue && newValue.length > 0) {
// Product uses containers — build multi-level navigation
newValue.forEach(container => {
console.log(container.containerName); // "Interior"
console.log(container.hasErrors); // Aggregated from all groups
console.log(container.values); // Array of group selection data
});
} else {
// No containers — use blockGroups directly for single-level navigation
}
});
Key container properties
| Property | Description |
|---|---|
containerId | Unique identifier |
containerName | Display name (e.g., "Exterior", "Interior", "Performance") |
value | Array of group IDs belonging to this container |
groupNames | Array of group names for display purposes |
values | Aggregated selection data from all groups (with hasErrors per group) |
hasErrors | true if any group in this container has validation errors |
Containers are optional. Many products won't have them. Always check if blockGroupContainers emits a non-empty array before building multi-level navigation — and fall back to blockGroups for single-level layout.
Selected Options
The optionSets.selectedOptions observer gives you the current configuration state — what's selected across all blocks:
window.mimeeqApp.observers.optionSets.selectedOptions.subscribe(({ newValue }) => {
if (newValue) {
// For standard products, all selections are under "SINGLE_PRODUCT_ID"
const options = newValue['SINGLE_PRODUCT_ID'];
options.forEach(option => {
console.log(option.blockId); // Which block this selection belongs to
console.log(option.name); // Display name of the selected option
console.log(option.code); // Configuration code value
});
}
});
For modular products, the keys are instance IDs instead of "SINGLE_PRODUCT_ID", with one entry per component on the scene:
// Modular: iterate over all component instances
Object.entries(newValue).forEach(([instanceId, options]) => {
console.log(`Component ${instanceId}: ${options.length} options selected`);
});
How It All Connects
Here's a practical example of how these layers relate. Consider a configurable office chair:
Container: "Seating" (only used in custom UIs)
- Group: "Frame & Base"
- Block: "Frame Color" (widget:
THUMBNAILS, mode:LINKED)- Options: "Black", "Silver", "White" (from a shared Material Collection)
- Block: "Base Type" (widget:
RADIO_PLAIN_TEXT, mode:LINKED)- Options: "5-Star", "4-Leg", "Sled"
- Block: "Frame Color" (widget:
- Group: "Upholstery"
- Block: "Seat Fabric" (widget:
FILTER, mode:LINKED)- Options: 200+ fabrics from a Material Collection with filter attributes
- Block: "Back Fabric" (widget:
FILTER, mode:LINKED)- Options: same Material Collection, with
selectMatchinglinking to Seat Fabric
- Options: same Material Collection, with
- Block: "Seat Fabric" (widget:
- Group: "Personalization"
- Block: "Nameplate" (widget:
ENGRAVE, mode:ENGRAVE)- User enters custom text
- Block: "Logo" (widget:
IMAGE, mode:IMAGE)- User uploads an image
- Block: "Nameplate" (widget:
The standard Mimeeq UI renders the Groups and Blocks. If you're building a custom UI and the product has Containers, you can use them to create a richer navigation structure — tabs, accordions, or a step-by-step wizard.
Related Documentation
- Observers Reference — Complete list of all observers including
optionSets.* - Programmatic Configuration Control — Change options, set configurations, and control blocks via code
- Short Codes — How configuration codes encode block-option pairs
- Custom UI Guide — Building complete custom interfaces using these data structures
- Headless Configurator Guide — Using observers and actions without the standard UI