iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Use an as prop when a component should choose its rendered element from a prop; use Radix’s asChild when a caller-supplied child should become that element and receive the component’s props and behavior. Neither is a built-in React API. The right choice depends on who should control the target, how its props and ref are handled, and whether the rendered element preserves the expected semantics.
What is the difference between `as` and `asChild`?
| Question | as |
Radix asChild |
|---|---|---|
| Who chooses the rendered target? | The wrapper chooses it through the as prop. |
The caller supplies a child that becomes the rendered target. |
| How do props and behavior reach it? | The wrapper renders the selected target and passes props to it. | Radix omits its default element and clones the child with props and behavior merged onto it. |
| What is the main TypeScript concern? | Connect the selected target to its props, commonly with a generic target type and target-derived props. | The child must accept and pass through injected props and any ref the primitive needs. |
| Where can the pattern go wrong? | A target may not support the wrapper’s behavior or semantics. | The child may fail to forward props or refs, or may not support the primitive’s interactions. |
These are different component design choices, not interchangeable syntax. A wrapper with as exposes target selection as part of its own API. With asChild, the caller writes the target element or component and the primitive composes onto it.
How do I type a polymorphic React component with an `as` prop?
There is no official React or TypeScript canonical polymorphic utility type. A common design is to make the target a generic type parameter, derive the target’s props, omit keys owned by the wrapper where necessary, and then add the wrapper’s own props.
type ActionOwnProps = {
tone?: "primary" | "quiet";
};
type PolymorphicProps<C extends React.ElementType, OwnProps> =
OwnProps &
{ as?: C } &
Omit<React.ComponentPropsWithoutRef<C>, keyof OwnProps | "as">;
type ActionProps<C extends React.ElementType = "button"> =
PolymorphicProps<C, ActionOwnProps>;
declare function Action<C extends React.ElementType = "button">(
props: ActionProps<C>
): React.ReactElement | null;
This type shape expresses the key relationship: when the caller selects an anchor, the available target props should come from an anchor; when the default button is used, button props apply. The example describes a public type shape, not a complete implementation. A real component must render the selected target and pass the intended props and ref to it.
#1 Best Overall
Handle prop collisions deliberately
Omit removes target props whose names the wrapper owns, so the wrapper’s contract controls those names. This is a design decision: if a wrapper prop overlaps with a target prop, document which meaning wins and how the value is applied. Avoid silently exposing two incompatible meanings under one prop name.
Constrain the targets to the component’s job
A generic type can permit many targets, but that does not mean every target is appropriate. Limit or document supported targets so consumers cannot select an element that lacks the keyboard, focus, pointer, or accessibility behavior the component promises. A button-like control should not casually become a non-interactive element merely because the type permits it.
Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
How does Radix `asChild` work?
Radix documents asChild on primitive parts that render a DOM element. When enabled, the primitive omits its default element and clones its child, merging in the props and behavior it needs. For example, a Tooltip trigger that normally renders a button can compose onto an anchor. See the Radix Composition guide.
Free tools Windows power users keep installed
One-click scans. No signup required.
Radix Slot provides the composition mechanism: use Slot.Root when asChild is true and render the normal element otherwise. Slot merges props onto its immediate child. If the wrapper has multiple children, Radix documents Slottable to mark which child receives the merged props. The Slot documentation identifies the documented Slot version as 1.3.0; check the API against the package version installed in your project.
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
type ActionProps = React.ComponentPropsWithoutRef<"button"> & {
asChild?: boolean;
};
function Action({ asChild = false, ...props }: ActionProps) {
const Comp = asChild ? Slot.Root : "button";
return <Comp {...props} />;
}
This simplified wrapper shows the selection pattern. Its props are typed as button props, so it does not provide the same target-derived generic API as a fully polymorphic as component. More importantly, the child supplied under asChild must accept the injected props and preserve the interaction behavior the primitive requires.
How do I forward props and refs with Radix `asChild`?
A custom component used as the child must pass received props to the underlying element and accept the ref when the primitive needs to attach one. If it drops event handlers, accessibility attributes, or other injected props, composition can break. Radix recommends that leaf components support refs so composition does not depend on their internal implementation details.
React 18 and earlier-compatible ref pattern
For code targeting React versions before 19, Radix’s documented pattern uses React.forwardRef:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsconst LinkButton = React.forwardRef<
HTMLAnchorElement,
React.ComponentPropsWithoutRef<"a">
>((props, ref) => {
return <a {...props} ref={ref} />;
});
Spread the props onto the actual element and attach the received ref. Do not consume a prop needed by the primitive unless the component deliberately handles and forwards its behavior.
Best Value
React 19 function-component ref pattern
React 19 lets function components receive ref as a prop, so new function components no longer need forwardRef. The current React forwardRef reference marks it deprecated for React 19 in favor of passing ref as a prop. Keep the code and types aligned with the React and @types/react major versions your project supports; do not assume a React 19 ref signature works unchanged in earlier versions.
React 19 also has TypeScript migration changes, including use of the scoped React.JSX namespace rather than reliance on the global JSX namespace. Consult the React 19 upgrade guide when updating types. React treats key and ref specially rather than as ordinary props in the historical model; see React’s special props warning for the relevant distinction.
How do I choose a safe rendered element?
Both patterns can produce inappropriate markup if the selected target does not support the component’s expected behavior. With Radix composition, the child has a particularly direct responsibility: a Tooltip trigger’s child, for example, must be focusable and respond to pointer and keyboard events. Radix cautions that changing a focusable trigger to a div can make it inaccessible.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →- Use an element that supports the primitive’s intended keyboard and pointer interactions.
- Preserve focusability and the semantics users and assistive technology rely on.
- For a custom child component, pass through injected props and the ref when required.
- Keep the wrapper’s supported targets narrow enough that its promised behavior remains meaningful.
Radix summarizes the responsibility this way: “If you do decide to change the underlying element type, it is your responsibility to ensure it remains accessible and functional.” — Radix UI Composition guide.
Should I use `as` or `asChild` for a Button?
Use as when the Button API should own target selection and expose props derived from that target. Use asChild when callers should provide an existing element or component—such as a navigation link—and the Button should compose its styling and behavior onto that child. Choose based on the API contract your design system needs, not on a claim that one pattern is universally better.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

