# Composition
> An overview of component composition patterns in Vera React.

## Compound components

The compound components pattern in React is a design strategy for building flexible and reusable components.
Instead of creating a single, monolithic component that tries to handle every possible variation through an ever-growing number of props, you break it down into multiple, interconnected components that work together. These components implicitly share state and logic, often using [React Context](https://react.dev/reference/react/createContext) behind the scenes.

Transitioning from a monolithic component to compound components inverts control, giving consumers granular access to each part of the larger component as needed. This allows them to:

- Compose individual parts with other components or logic.
- Control the rendering order and structure.
- Add custom props, event listeners, or refs directly to the relevant sub-component.
- Apply contextual styles.

This approach strikes a balance between simplicity and flexibility. In simple, common cases, consumers can use the components with minimal configuration.
However, components remain open for extension and composition with other elements, adapting gracefully to a variety of use cases.

Below is an example of using [Popover](/components/popover) and [Form Field](/components/forms/form-field) components to demonstrate the pattern.
You compose the desired structure using the provided parts while allowing customization of each part:

```tsx
<Popover.Root>
  <Popover.Trigger as={Button} variant="outline">
    {'Open Popover'}
  </Popover.Trigger>
  <Popover.Content placement="top_left" css={{ width: '24rem' }}>
    <Flex as="form" flow="column" gap="spacingM">
      <FormField.Root as={Flex} flow="column">
        <FormField.Label>{'First name'}</FormField.Label>
        <TextInput />
      </FormField.Root>
      <FormField.Root as={Flex} flow="column">
        <FormField.Label>{'Last Name'}</FormField.Label>
        <TextInput />
      </FormField.Root>
      <Flex main="end">
        <Popover.Close as={Button} variant="ghost">
          {'Cancel'}
        </Popover.Close>
        <Button type="submit">{'Submit'}</Button>
      </Flex>
    </Flex>
  </Popover.Content>
</Popover.Root>
```

## Polymorphism

Polymorphism in React allows components to change their underlying rendered element or compose with other components while preserving their core functionality.

Vera components support common pattern for achieving polymorphism: the `as` prop. This pattern further enhances reuse and versatility of the components, while being type-safe because props can be correctly inferred based on the provided element or component.

`as` prop is especially handy when your goal is to swap out the default HTML element while maintaining the component's behavior and styling. For example, to render a [Button component](/components/button) as an anchor element:

```tsx
<Button
  as="a"
  variant="outline"
  rightIcon={<SvgIcon iconName="arrowRight" />}
  href="https://geminisuite.com"
  target="_blank"
>
  {'Go to geminisuite.com'}
</Button>
```

In the example above, the `Button` component renders as an `<a>` tag, inheriting all the props specific to anchor elements, such as `href` and `target`.

The `as` prop can also be used to render another component, enabling composition of behavior:

```tsx
<Popover.Root>
  <Popover.Trigger as={Button} variant="outline">
    {'Open Popover'}
  </Popover.Trigger>
  <Popover.Content placement="top_left">
    {'Popover content'}
    <Box marginTop="spacingM">
      <Popover.Close as={Button} variant="outline">
        {'Close'}
      </Popover.Close>
    </Box>
  </Popover.Content>
</Popover.Root>
```

In the example above, the `Popover.Trigger` and `Popover.Close` render as the `Button` component, inheriting its props while maintaining its own behavior.

In frameworks like [Next.js App Router](https://nextjs.org/docs/app),
you can use `as` to render [Next.js Link component](https://nextjs.org/docs/pages/api-reference/components/link) that handles routing. This is useful when you want to maintain the behavior of a Vera component while also providing navigation functionality:

```tsx
import Link from 'next/link';

function MainNav() {
  return (
    <SidebarNavigation.Root>
      <SidebarNavigation.Item
        as={Link}
        href="/dashboard"
        label="Dashboard"
        iconName="home"
      />
    </SidebarNavigation.Root>
  );
}
```

The `as` prop has some limitations by default, as it only accepts a single element or component. However, you can use the `as` prop with a [Slot utility](/components/utility/slot), allowing for composing more than two components together:

```tsx
<Dialog.Root>
  <Dialog.Trigger as={Slot}>
    <Link as="button">{'Dialog trigger with a link appearance'}</Link>
  </Dialog.Trigger>
  <Dialog.Box padding="spacingM">
    {'Dialog content'}
    <Box marginTop="spacingM">
      <Dialog.Close as={Button} variant="outline">
        {'Close'}
      </Dialog.Close>
    </Box>
  </Dialog.Box>
</Dialog.Root>
```

The trade-off is that you lose some type safety compared to passing component directly to `as` prop.
With `Slot` rendering is delegated to the child element or component, thus the strict contract between parent and child is lost.

### Best practices for creating compatible components

When creating your own custom components that are intended to work seamlessly as targets for the `as` prop, follow these practices to make them open for extension:

- Ensure your component spreads all incoming props onto the underlying DOM element or component it renders.
- Chain the event props with the internal event handlers, if any.
- Combine any incoming `style` or `className` props with your component's internal styles or classes.
- Use `React.forwardRef` to accept a ref and forward it to the underlying DOM element. If your component uses an internal ref, merge the forwarded ref with your internal one.

For example:

```tsx
const MyButton = React.forwardRef((props, forwardedRef) => (
  <button
    ref={forwardedRef}
    {...props}
    style={{
      position: 'relative',
      ...props.style
    }}
    onClick={event => {
      props.onClick?.(event);
      // Additional behavior here
    }}
  />
));

// Usage
<Dialog.Trigger as={MyButton}>Open dialog</Dialog.Trigger>;
```
