Skip to content

Migrating to v2 ​

v2.0.0 changes three behaviors and adds one required peer dependency. Most codebases only need the install step.

Install the new peer dependency ​

@emotion/css is now required. It backs className() and classNames, and it is imported unconditionally — so it is needed even if you only ever use the css prop.

diff
- pnpm add emotion-variants @emotion/react
+ pnpm add emotion-variants @emotion/react @emotion/css

undefined now falls back to defaultVariants ​

In v1 the config and props were merged with object spread, so an explicit undefined erased the default. v2 resolves each key with ??.

ts
const button = ev({
  variants: {
    size: {
      small: {},
      large: {},
    },
  },
  defaultVariants: {
    size: 'small',
  },
});

button({ size: undefined });
// v1: no size style at all
// v2: 'small'

What to remove. Any workaround that stripped undefined before calling, or that duplicated the default at the prop boundary:

diff
  function Button({ size }: Props) {
-   const variants = size !== undefined ? { size } : {};
-   return <button css={button(variants)}>Click</button>;
+   return <button css={button({ size })}>Click</button>;
  }
diff
- function Button({ size = 'small' }: Props) {
+ function Button({ size }: Props) {
    return <button css={button({ size })}>Click</button>;
  }

false and 0 are unaffected — they are real variant values and were never replaced by the default.

The call always returns an array ​

v1 returned Interpolation<unknown>, which was a bare SerializedStyles object when exactly one style matched. v2 always returns SerializedStyles[].

diff
- (variants?: EvVariants<T>): Interpolation<unknown>;
+ (variants?: EvVariants<T>): SerializedStyles[];

The css prop accepts both, so css={button(...)} needs no change. Code that inspected the result does:

diff
- const styles = button({ size: 'large' });
- const list = Array.isArray(styles) ? styles : [styles];
+ const list = button({ size: 'large' });

classNames returns real class names ​

In v1 every entry was the string "[object Object]" — String() applied to a SerializedStyles. v2 injects each style through @emotion/css and returns the generated class.

ts
button.classNames.variants.size.small;
// v1: '[object Object]'
// v2: 'css-def456'

Nothing is injected until you first read classNames, and the result is memoized, so css-prop-only code pays nothing.

If you worked around the old behavior by building class names yourself, delete that code. Note that classNames still covers base and single variant values only — for compound variants use className().

New: className(variants) ​

v2 adds a method returning one class for the whole combination, including compound variants. Useful for className props and non-React code.

tsx
<button className={button.className({ size: 'large', color: 'primary' })}>Click me</button>;

New: EvClassNames type ​

EvClassNames<T> is now exported. See Types.

Released under the MIT License.