---
title: "TypeScript"
description: "Infer variant props with VariantProps, tighten required keys, and type slotted return values."
canonical_url: "https://vinicunca.dev/unocss-variants/concepts/typescript"
---
# TypeScript

> Infer variant props with VariantProps, tighten required keys, and type slotted return values.

UnoCSS Variants is written in TypeScript. Variant keys and values infer from the object you pass to `uv` — no codegen.

## `VariantProps`

Extract the props a recipe accepts. `class` and `className` are omitted so you can add your own host-prop type.

```ts
import type { VariantProps } from 'unocss-variants';
import { uv } from 'unocss-variants';

export const button = uv({
  base: 'px-4 py-1.5 rounded-full hover:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 color-white',
      neutral: 'bg-zinc-500 color-black dark:color-white'
    },
    flat: {
      true: 'bg-transparent'
    }
  },
  defaultVariants: {
    color: 'primary'
  },
  compoundVariants: [
    {
      color: 'primary',
      flat: true,
      class: 'bg-blue-500/40'
    },
    {
      color: 'neutral',
      flat: true,
      class: 'bg-zinc-500/20'
    }
  ]
});

type ButtonVariants = VariantProps<typeof button>;
// {
//   color?: 'primary' | 'neutral'
//   flat?: boolean
// }
```

Keys present in `defaultVariants` stay optional. Boolean axes (`true` / `false` keys) infer as `boolean`.

In Vue, extend the type for your component props:

```ts
interface ButtonProps extends ButtonVariants {
  class?: string;
}
```

## Required variants

There is no `required: true` flag. Use TypeScript utilities:

```ts
import type { VariantProps } from 'unocss-variants';
import { uv } from 'unocss-variants';

const buttonStyles = uv({
  base: 'px-4 py-1.5 rounded-full hover:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 color-white',
      neutral: 'bg-zinc-500 color-black dark:color-white'
    },
    requiredFlat: {
      true: 'bg-transparent',
      false: 'bg-white'
    }
  },
  defaultVariants: {
    color: 'primary'
  }
});

type ButtonVariants = VariantProps<typeof buttonStyles>;

export interface ButtonProps
  extends Omit<ButtonVariants, 'requiredFlat'>,
  Required<Pick<ButtonVariants, 'requiredFlat'>> {}

export function button(props: ButtonProps) {
  return buttonStyles(props);
}

// @ts-expect-error missing requiredFlat
button({});

button({ requiredFlat: true });
```

Leaving an axis out of `defaultVariants` also keeps it required in practice at the type level if you model it that way — `VariantProps` still marks every variant key optional because the runtime accepts a missing key (it just skips that axis). Use `Required<Pick<...>>` when the UI must not render without a value.

## Slotted return types

A recipe with `slots` returns an object of functions. `ReturnType<typeof recipe>` is that object:

```ts
const alert = uv({
  slots: {
    base: 'flex gap-3 rounded-lg p-4',
    title: 'font-semibold',
    description: 'text-sm'
  },
  variants: {
    color: {
      default: {
        base: 'bg-zinc-100',
        title: 'color-zinc-900'
      },
      danger: {
        base: 'bg-red-50',
        title: 'color-red-900'
      }
    }
  }
});

type AlertSlots = ReturnType<typeof alert>;
// { base: (props?) => string; title: (props?) => string; description: (props?) => string }

function classesFor(color: VariantProps<typeof alert>['color']) {
  const { base, title, description } = alert({ color });
  return { base: base(), title: title(), description: description() };
}
```

The exported return type name is **UvReturnType** (not `UVReturnType`). It is a callable plus metadata: `base`, `slots`, `variants`, `variantKeys`, `defaultVariants`, `compoundVariants`, `compoundSlots`, and `extend`.

## `as const` for external maps

When variant maps live outside `uv`, use `as const` so keys stay literal:

```ts
const colors = {
  primary: 'bg-zinc-900 color-white',
  secondary: 'bg-zinc-100 color-zinc-900'
} as const;

const button = uv({
  variants: {
    color: colors
  }
});
```


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
