{"id":28195518,"url":"https://github.com/nasheomirro/prop-variants","last_synced_at":"2026-02-25T18:07:10.590Z","repository":{"id":290709819,"uuid":"974887681","full_name":"nasheomirro/prop-variants","owner":"nasheomirro","description":"A neat way to organize your variant-like props","archived":false,"fork":false,"pushed_at":"2025-05-03T07:49:20.000Z","size":38,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-21T20:20:33.614Z","etag":null,"topics":["classnames","props-type","variants"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nasheomirro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-29T13:05:41.000Z","updated_at":"2025-05-03T07:48:34.000Z","dependencies_parsed_at":"2025-04-30T06:46:44.501Z","dependency_job_id":null,"html_url":"https://github.com/nasheomirro/prop-variants","commit_stats":null,"previous_names":["nasheomirro/props-map"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nasheomirro/prop-variants","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasheomirro%2Fprop-variants","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasheomirro%2Fprop-variants/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasheomirro%2Fprop-variants/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasheomirro%2Fprop-variants/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nasheomirro","download_url":"https://codeload.github.com/nasheomirro/prop-variants/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasheomirro%2Fprop-variants/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29833824,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T17:57:15.019Z","status":"ssl_error","status_checked_at":"2026-02-25T17:56:11.472Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["classnames","props-type","variants"],"created_at":"2025-05-16T14:13:26.506Z","updated_at":"2026-02-25T18:07:10.576Z","avatar_url":"https://github.com/nasheomirro.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# prop-variants\n\nA small set of utility types that helps you deal with exposing variants through props.\n\n## Assumption\n\nBefore we get into it, the library assumes you want to map your variants into an object similar to this:\n\n```ts\nconst variants = {\n  size: {\n    sm: \"\",\n    md: \"\",\n    lg: \"\",\n  },\n  theme: {\n    primary: \"\",\n    secondary: \"\",\n  },\n};\n\n// which translates to...\ntype Props = {\n  size: \"sm\" | \"md\" | \"lg\";\n  theme?: \"primary\" | \"secondary\";\n};\n```\n\nNow, the goal for this package is to provide types that **\"connect\"** this object to your props, or vice versa, as well as adding a few things to really organize how you use this object.\n\n## How to use\n\nTo map out your `Props` to your variants, you can use the `ToVariants` utility type:\n\n```ts\nimport type { ToVariants } from \"prop-variants\";\n\ntype Props = {\n  size?: \"sm\" | \"md\" | \"lg\";\n  theme: \"primary\" | \"secondary\";\n};\n\n// makes this type-safe!\nconst variants = {\n  size: {\n    sm: \"\",\n    md: \"\",\n    // complains that there is no lg!\n  },\n  theme: {\n    primary: \"\",\n    secondary: \"\",\n  },\n} satisfies ToVariants\u003cProps\u003e;\n```\n\nThis provides type-safety and auto-suggestions to the `variants` object. Note that you should always use the `satisfies` keyword to preserve the full type of your object. Now if you want to do this in reverse; where your variants create your `Props`, take a look at the [`ToKeyMap` type](#creating-props-based-on-variants).\n\n### Dirty `Props`\n\nSometimes your props aren't just made up of variant mappings, there are other stuff in there that isn't related to variants at all. Now instead of separating your `Props` to accomodate for your variant mappings, `ToVariants` goes around this by being strict on what it allows to become a variant:\n\n```ts\nimport type { ToVariants } from \"prop-variants\";\n\ntype Props = {\n  theme?: \"primary\" | \"secondary\";\n  size?: \"sm\" | \"md\" | \"lg\";\n  text: string;\n  onclick?: () =\u003e void;\n  disabled?: boolean;\n};\n\n// only \"theme\" and \"size\" is considered a variant!\nconst variants = {\n  theme: { primary: \"\", secondary: \"...\" },\n  size: { sm: \"\", md: \"\", lg: \"...\" },\n} satisfies ToVariants\u003cProps\u003e;\n```\n\nIt will ignore the other props in there that doesn't qualify as a variant, this is because `ToVariants` only accept values that are `\"string instances\"`.\n\n### Variants aren't just strings\n\nYou can create variant values that take any form. Although the main use case for this project was for class strings, it's really just a mapping of any \"variant-like\" props you want to expose to the consumer:\n\n```ts\nimport type { ToVariants } from \"prop-variants\";\n\ntype Props = {\n  size?: \"sm\" | \"md\" | \"lg\";\n  theme?: \"primary\" | \"secondary\";\n  behavior?: \"instant\" | \"debounced\";\n};\n\nconst variants = {\n  size: {\n    sm: { container: \"\", text: \"\" },\n    md: { container: \"\", text: \"\" },\n    lg: { container: \"\", text: \"\" },\n  },\n  theme: {\n    primary: \"\",\n    secondary: \"\",\n  },\n  behavior: {\n    instant: () =\u003e {},\n    smooth: () =\u003e {},\n  },\n} satisfies ToVariants\u003cProps\u003e;\n```\n\n### Creating `Props` based on variants\n\nNow so far we used `ToVariants\u003cProps\u003e` to build out our variants object. But we could also do that in reverse with `ToKeyMap`:\n\n```ts\nimport type { ToKeyMap } from \"prop-variants\";\n\nconst variants = {\n  size: {\n    sm: \"\",\n    md: \"\",\n    lg: \"\",\n    /* ... */\n  },\n  theme: {\n    primary: \"\",\n    secondary: \"\",\n    /* ... */\n  },\n};\n\n// note the second argument \"theme\"\ntype Props = ToKeyMap\u003ctypeof variants, \"theme\"\u003e;\n\n// which translates to...\ntype Props = {\n  size: \"sm\" | \"md\" | \"lg\";\n  theme?: \"primary\" | \"secondary\";\n};\n```\n\nThe `Props` should now automatically update when variants changes. Note that for optional props we need to manually tell `ToKeyMap` which variant should become optional, this is because `ToKeyMap` has no way to know just from `typeof variants`.\n\nThis is useful but this leaves `variants` untyped, because of this it might be better to still use `ToVariants` instead.\n\n## Recipes\n\nSome functions that might be helpful in certain conditions. Currently, the package does not contain any of these functions so if you want to use them you'll have to declare them yourself.\n\n### `map()` function\n\nSay you wanted to grab from the props the values for the variants, here's a function to do just that:\n\n```ts\nimport type { AnyObject, VariantGroup, ToVariants, ToValueMap, GetOptionalKeys } from \"prop-variants\";\n\nfunction map\u003cP extends AnyObject, T extends ToVariants\u003cP\u003e = ToVariants\u003cP\u003e\u003e(\n  props: P,\n  variants: T\n): ToValueMap\u003cT, GetOptionalKeys\u003cP\u003e extends keyof T ? GetOptionalKeys\u003cP\u003e : never\u003e;\nfunction map\u003cP extends AnyObject, T extends VariantGroup\u003e(props: P, variants: T): AnyObject {\n  const values: AnyObject = {};\n  for (let key of Object.keys(variants) as any[]) {\n    if (Object.prototype.hasOwnProperty.call(props, key)) {\n      values[key] = variants[key][props[key]];\n    } else {\n      values[key] = undefined;\n    }\n  }\n\n  return values;\n}\n```\n\nThis would return the computed values for every variant inside the given `variants` object depending on the given `props`. Note that you do not have to \"clean\" your props for this, just like `ToVariants` it will ignore anything that isn't a variant.\n\n### `values()` function\n\nSimilar to the `map` function, will grab from the props the values for the variants, but instead of an object it would just be an array of values. This is useful in tandem with `clsx`. Note that the order of the values are random, so keep that in mind if you are using `tailwind-merge`.\n\n```ts\nimport type { AnyObject, VariantGroup, ToVariants, ToValueArray } from \"prop-variants\";\n\nfunction values\u003cP extends AnyObject, T extends ToVariants\u003cP\u003e = ToVariants\u003cP\u003e\u003e(\n  props: P,\n  variants: T\n): ToValueArray\u003cT, undefined extends P[keyof P] ? true : never\u003e;\nfunction values\u003cP extends AnyObject, T extends VariantGroup\u003e(props: P, variants: T): any[] {\n  const values = [];\n  for (let key of Object.keys(variants) as any[]) {\n    if (Object.prototype.hasOwnProperty.call(props, key)) {\n      values.push(variants[key][props[key]]);\n    } else {\n      values.push(undefined);\n    }\n  }\n\n  return values;\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnasheomirro%2Fprop-variants","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnasheomirro%2Fprop-variants","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnasheomirro%2Fprop-variants/lists"}