Skip to main content

Variants

Check out the Variants Aspect on Bit.dev

teambit.workspace/variants enables cascading configurations by selection of component groupings or sets in the workspace and applying configurations to these cascading groups. Configurations set on a certain set of components can:

  1. Affect only the selected set of components
  2. Inherit policies set on a more general set of components (including the workspace default configs)
  3. Override conflicting configurations inherited from more general component selections
  4. Propagate configurations downwards to more specific sub-sets of components

Variants Selector Examples#

Select a rule set by directory#

To select a set using a directory path, use the relative path to the components' parent directory from the workspace root. In the following example, all components under the components/utility-functions directory (and any sub-directories) will be included in this set:

"teambit.workspace/variants": {    "components/utility-functions": {        "teambit.harmony/node": {}    },}

Select a rule set via namespace#

This option is recommended as it decouples your components' configurations from the workspace's file structure. It handles components using fundamental definitions that pertain to function and purpose, via their namespace. The namespace selector behaves like a glob pattern, with the component name (including its namespace) being the equivalent of a file path being matched against the pattern. Specifically, this means that namespace selectors support the location-specific * matcher, and the 'anywhere' ** matcher for matching the component name.

In the following example, any component under the utility-functions namespace (and it's sub-namespaces) will be included in this rule set:

"teambit.workspace/variants": {    "{utility-functions/**}": { // Match any component name starts with utility-functions        "teambit.harmony/node": {}    },}

In the following example however, only components directly under the utility-functions namespace will be included in this rule set:

"teambit.workspace/variants": {    "{utility-functions/*}": { // Match utility-functions/sort-array but not utility-functions/string/reverse        "teambit.harmony/node": {}    },}

Grouping selectors#

You can add several sets for the same variant configuration by grouping selectors together:

Multiple directory paths
"teambit.workspace/variants": {    "components/utils, components/react-ui": {        "teambit.harmony/node": {}    },}
Multiple namespaces
"teambit.workspace/variants": {    "{utility-functions/**}, {react-ui/**}": {        "teambit.harmony/node": {}    },}
Paths and namespaces
"teambit.workspace/variants": {    "{utility-functions/**}, {react-ui/**}, components/utils, components/react-ui": {        "teambit.harmony/node": {}    },}

Exclude directories/components from a rule#

Using the ! deselector you can exclude a set of components from a selector. The ! deselector works both for directories and namespaces, for example:

Exclude a sub-directory directory from a rule#

For example, apply the teambit.harmony/node environment on the utility-functions set, but exclude the utility-functions/react-utils folder from that set:

"workspace.json
"teambit.workspace/variants": {    "components/utility-functions, !components/utility-functions/react-utils": {        "teambit.harmony/node": {}    },}

Exclude namespaces from a rule#

The following example applies the teambit.harmony/node environment on every component under the utils namespace, but excludes the utils/react namespace and its children from this set:

"workspace.json
"teambit.workspace/variants": {    "{utils/**}, !{utils/react/**}": {        "teambit.harmony/node": {}    },}

Special Variants#

The Wildcard (*) variant#

To select all components in your workspace use the wildcard variant *. This is useful when you want to apply very general configurations, especially default or backup configurations, on all components. Using this selector can produce unexpected consequences if the rules aren't general enough, so we recommend using this selector sparingly! For example:

"teambit.workspace/variants": {    "*": {        "teambit.harmony/node": {}    },}

Merging Configurations#

The same component may have several rules applied to it, including several versions of the same configuration. This works very much like CSS rules where rules cascade but the more specific variant "wins" when there are rule 'conflicts'.

The following example shows how Bit does not apply aspect1-components-key nor the aspect1-root-key for components under the components/ui directory, as the my-aspect1 extension was re-set by a more specific variant.

"workspace.json
{  "teambit.workspace/variants": {    "*": {      "my-aspect1": {        "aspect1-root-key": "aspect1-root-val"      },      "my-aspect2": {        "aspect2-root-key": "aspect2-root-val"      },      "my-aspect4": {        "aspect4-root-key": "aspect4-root-val"      }    },    "components": {      "my-aspect1": {        "aspect1-components-key": "aspect1-components-val"      },      "my-aspect2": {        "aspect2-components-key": "aspect2-components-val"      }    },    "components/ui": {      "my-aspect1": {        "aspect1-components-ui-key": "aspect1-components-ui-val"      },      "my-aspect3": {        "aspect3-components-ui-key": "aspect3-components-ui-val"      }    }  }}
components/ui/button's calculated configuration
{  "my-aspect1": {    "aspect1-components-ui-key": "aspect1-components-ui-val"  },  "my-aspect2": {    "aspect2-components-key": "aspect2-components-val"  },  "my-aspect3": {    "aspect3-components-ui-key": "aspect3-components-ui-val"  },  "my-aspect4": {    "aspect4-root-key": "aspect4-root-val"  }}

Variants Flags#

Propagate#

When using selectors which can propagate down to sub-sets, such as with directory selectors (where all sub-directories are included) or {namespace/**} type selectors, you can prevent this propagation for specific set of inheritors, by setting set the propogate value of an inheriting group of components to false. Once bit sees "propagate": false it uses only the configuration for this set and does not inherit.

"workspace.json
"teambit.workspace/variants": {  "components/react": {    "my-aspect2": {      "aspect2-react-key": "aspect2-react-val"    }  },  "components/react/ui": {    "propagate": false, // take this config, and don't propagate parent configs down to here    "my-aspect1": {      "aspect1-react-ui-key": "aspect1-react-ui-val"    }  }}
components/react/ui/button's calculated configuration
{  "my-aspect1": {    "aspect1-react-ui-key": "aspect1-react-ui-val"  }}

Removing aspects#

Note: Once a component has been tagged, any aspect configured for that component can only be removed from the component via the following remove method. (if you haven't exported yet then untag would reset the effect of the tag)

There are numerous scenarios where you would not want a specific aspect to be defined on a subgroup but you don't want to exclude the sub-group from upstream rules, or use the propagate: false flag, since you want to receive the other configurations from the parent group rule/s.

In that case, removing a specific aspect can be achieved using "-" as the value for an aspect's configuration. This will remove this aspect from the current component set.

For instance, the following will remove my-aspect2 from components in the components/react/ui set, while still inheriting other configs such as the my-aspect3 aspect.

"workspace.json
"teambit.workspace/variants": {  "components/react": {    "my-aspect2": {      "aspect2-react-key": "aspect2-react-val"    },    "my-aspect3": {      "aspect3-react-key": "aspect3-react-val"    }  },  "components/react/ui": {    "my-aspect1": {      "aspect1-react-ui-key": "aspect1-react-ui-val"    },    "my-aspect2": "-" // Remove my-aspect2 from this set's configuration  }}
components/react/ui/button's calculated configuration
{  "my-aspect1": {    "aspect1-react-ui-key": "aspect1-react-ui-val"  },  "my-aspect3": {    "aspect3-react-key": "aspect3-react-val"  }}