Variants

'Variants' enables you to configure groups of components with component configuration. Components are selected using their common directory path or namespace, and applied, collectively, with configurations (i.e, pairs of Aspects and their corresponding configs).

Since each component can be selected more than once using different selectors, Variants looks for all available configurations and merges them together.

Use Variants as an efficient alternative to manually configuring each component's component.json.

Variants Selector Examples

Configure components in a directory

To select a group of components 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:

This examples configures all components contained in the design directory with the React env.

"teambit.workspace/variants": {
    "design": {
        "teambit.react/react": {}
    },
}
CopiedCopy

Configure components using Namespaces

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": {}
    },
}
CopiedCopy

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": {}
    },
}
CopiedCopy

Grouping selectors

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

"teambit.workspace/variants": {
    "components/utils, components/react-ui": {
        "teambit.harmony/node": {}
    },
}
CopiedCopy
"teambit.workspace/variants": {
    "{utility-functions/**}, {react-ui/**}": {
        "teambit.harmony/node": {}
    },
}
CopiedCopy
"teambit.workspace/variants": {
    "{utility-functions/**}, {react-ui/**}, components/utils, components/react-ui": {
        "teambit.harmony/node": {}
    },
}
CopiedCopy

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:

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

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:

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

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": {}
    },
}
CopiedCopy

Merging Configurations

The merging process is similar to the way CSS rules are handled. Rules are cascaded from the selected group to all its members. When the same component is applied with conflicting configs (using multiple selectors), the most specific variant (selector) overrides the more general ones.

In the following example, my-org.my-scope/ui/inputs/button is selected using two different selectors ({ui\**} and {*\inputs\**}), and configured with conflicting configurations.

{
  "teambit.workspace/variants": {
    "{ui/**}": {
      "teambit.preview/preview": {
        "disabled": true
      }
    },
    "{*/inputs/**}": {
      "teambit.preview/preview": {
        "disabled": false
      }
    }
  }
}
CopiedCopy

The more specific selector "{*/inputs/*}" will override the more general one "{ui/**}". This will result in configuring my-org.my-scope/ui/inputs/button with "teambit.preview/preview": {"disabled": false}.

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 propagate 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.

"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"
    }
  }
}
CopiedCopy
{
  "my-aspect1": {
    "aspect1-react-ui-key": "aspect1-react-ui-val"
  }
}
CopiedCopy

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.

"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
  }
}
CopiedCopy
{
  "my-aspect1": {
    "aspect1-react-ui-key": "aspect1-react-ui-val"
  },
  "my-aspect3": {
    "aspect3-react-key": "aspect3-react-val"
  }
}
CopiedCopy