Skip to content
v2.5.0
English
Русский
Documentation

Slot

Slot is the low-level polymorphic composition primitive behind VegaUI asChild APIs.

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
polymorphic-trigger.tsx · 44 lines
import type { CSSProperties } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Text,
} from '@vega-ui/react';

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
styled-link-action.tsx · 16 lines
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
low-level-wrapper-authoring.tsx · 34 lines
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 Slot when authoring primitives or wrappers.
  • Use asChild on existing VegaUI components in product code.

Slot vs direct element rendering

  • Use direct rendering when you do not need prop merging.
  • Use Slot when 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.