TypeScript

Most components in @atom-learning/components are polymorphic: they render a sensible default element which you can change with the as prop. The accepted props and the ref type follow the element you pass — as="a" unlocks href and types the ref as HTMLAnchorElement.

<Box as="a" href="/about">About</Box>

// href is not valid on the default div — type error
<Box href="/about">About</Box>

Extracting prop types

React.ComponentProps works on any component in the library and returns the props for its default element, including any variant props with their exact unions.

import * as React from 'react'
import { Button } from '@atom-learning/components'

type ButtonSize = React.ComponentProps<typeof Button>['size']
// 'sm' | 'md' | 'lg' | 'xl' | ...

Extracting props for a different element

Props extracted with React.ComponentProps describe the default element only, so they exclude element-specific props such as href, and the extracted as key is intentionally unusable. To extract the props a component accepts when rendered as a different element, use StyledComponentProps and pass the element as the second argument.

import type { StyledComponentProps } from '@atom-learning/components'

// Box rendered as an anchor: includes href, ref is HTMLAnchorElement
type AnchorBoxProps = StyledComponentProps<typeof Box, 'a'>

Building your own polymorphic wrapper

A wrapper component that forwards as needs to declare it explicitly — the extracted props can't carry a polymorphic as, because the moment as changes, the native props and ref type change with it. Replace the key rather than intersecting it:

type CardProps = Omit<StyledComponentProps<typeof Box>, 'as'> & {
  as?: React.ElementType
}

const Card = ({ as, ...rest }: CardProps) => <Box as={as} {...rest} />

Note that a plain intersection (StyledComponentProps<typeof Box> & { as?: React.ElementType }) does not work — the two as declarations intersect to never. Always Omit the original key first, or narrow it to the elements your wrapper actually supports, for example as?: 'label' | 'legend'.