Patterns
Polymorphic Trigger
When to use:
- a low-level trigger primitive should render as a consumer-chosen element
Composition notes:
- guarantee exactly one child
- preserve the correct semantic element for the task
Trade-offs:
- flexible
- easier to misuse than fixed rendering
import type { CSSProperties } from 'react';
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogTrigger,
Heading,
Icon,
IconButton,
Text,
} from '@vega-ui/react';Styled Link Action
When to use:
- navigation should keep link semantics but borrow button styling
Composition notes:
- choose an
<a>element intentionally - keep navigation semantics explicit
Trade-offs:
- preserves link behavior
- easy to overuse when a plain link would be clearer
import { Button, Icon } from '@vega-ui/react';
import { BookOpenIcon } from '@vega-ui/icons';
export default function StyledLinkAction() {
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 'var(--spacing-4)' }}>
<Button type='button'>Create API key</Button>
<Button asChild variant='secondary' appearance='ghost'>
<a href='/docs/api/authentication'>
<Icon size='sm'><BookOpenIcon /></Icon>
Read the API guide
</a>Low-Level Wrapper Authoring
When to use:
- design-system internals need to merge behavior into one child
Composition notes:
- test event-handler merging carefully
- keep semantics explicit in the chosen child
Trade-offs:
- foundational for polymorphism
- easy to create subtle semantic regressions
import { type ReactNode, useState } from 'react';
import { Button, Slot } from '@vega-ui/react';
interface TrackClickProps {
event: string
onTrack(event: string): void
children: ReactNode
}
function TrackClick({ event, onTrack, children }: TrackClickProps) {
return <Slot onClick={() => onTrack(`analytics: ${event}`)}>{children}</Slot>;
}Comparison
Quick Decision Rule
Use Slot only when you are building low-level polymorphic composition. Prefer higher-level asChild APIs in normal product code.
Slot vs asChild on components
- Use
Slotwhen authoring primitives or wrappers. - Use
asChildon existing VegaUI components in product code.
Slot vs direct element rendering
- Use direct rendering when you do not need prop merging.
- Use
Slotwhen parent props must be merged into a chosen child element.
Choose This Component When
- you are extending design-system internals
- you need one-child prop merging semantics
Do Not Choose This Component When
- a component already exposes
asChild - you just need a normal element
Troubleshooting
The Composition Throws At Runtime
Symptom
Rendering fails with a Slot error about the child shape.
Likely Cause
The child is not exactly one valid React element.
How To Verify
- inspect what is actually passed as
children
Fix
Pass one valid React element, not plain text, arrays, or fragments with several nodes.
The UI Looks Right But Semantics Are Wrong
Symptom
The slotted element looks styled correctly, but behavior or accessibility is off.
Likely Cause
The chosen child element has the wrong semantics for the task.
How To Verify
- inspect the final rendered tag
- compare it with the intended semantic element
Fix
Pick the correct child element and remember that Slot only changes rendering, not semantics.
Event Behavior Feels Unexpected
Symptom
Handlers from parent and child seem to conflict.
Likely Cause
Prop merging created overlapping behavior.
How To Verify
- inspect the merged props on the final element
- test both parent and child handlers in isolation
Fix
Simplify the composition and make the ownership of interaction logic explicit.